diff --git a/pub/CHANGELOG b/pub/CHANGELOG
index e9e74e585de2fe2164cb86c487683f17af531cc8..5dddcf5d236e2969e6ccae72a021a31860289496 100644
--- a/pub/CHANGELOG
+++ b/pub/CHANGELOG
@@ -5,6 +5,7 @@ Release 2.3.0b of
   - Shell escape '!' restored
 - Improved behavior:
   - 'pn','pv' aka FK01,2 are ignored when j would exceed the allowed range
+  - Tests yield more explicit error messages; distinguish 'exit' from 'throw'
 - Improved help:
   - 'hr' for help on resolution convolution
 	
@@ -43,6 +44,7 @@ Release 2.2.3a of 2feb15:
 - New functionality:
   - global fits
   - integer expressions now evaluated in integer arithmetics
+  - starting a test script collection
 - Remove legacy methods:
   - rm loaders for old formats 96 and 01.
 - Major code refactoring:
diff --git a/pub/lib/fbase.cpp b/pub/lib/fbase.cpp
index 3facf934c868063659d1868d14df121744e39e91..aa24a58221ec134ce47dfe5189b48e3ac364fc12 100644
--- a/pub/lib/fbase.cpp
+++ b/pub/lib/fbase.cpp
@@ -168,8 +168,8 @@ double func_diehl( double a ) {
 
 int func_exit( int a ) { exit(a); return 0; }
 
-int func_exit_if( int a ) { if(a) exit(0); return 0; }
-int func_exit_unless( int a ) { if(!a) exit(0); return 0; }
+int func_throw_if( int a, string s ) { if(a) throw s; return 0; }
+int func_throw_unless( int a, string s ) { if(!a) throw s; return 0; }
 
 //**************************************************************************************************
 //*  Functions of two arguments
@@ -443,10 +443,6 @@ void fbase_initialize()
     G->register_fct_0_s  ( "pop", func_pop );
     G->register_fct_meta ( "exit", 1, "(x): exit(x)" );
     G->register_fct_i_i  ( "exit", func_exit );
-    G->register_fct_meta ( "exit_if", 1, "(x): exit(0) if x" );
-    G->register_fct_i_i  ( "exit_if", func_exit_if );
-    G->register_fct_meta ( "exit_unless", 1, "(x): exit(0) if !x" );
-    G->register_fct_i_i  ( "exit_unless", func_exit_unless );
     G->register_fct_meta ( "ln", 1, "(x): natural logarithm of x, or 0 if x<=0" );
     G->register_fct_d_d  ( "ln", func_ln );
     G->register_fct_e_e  ( "ln", func_ln );
@@ -557,14 +553,11 @@ void fbase_initialize()
     G->register_fct_meta ( "lndiehl", 1, "(cauchywid/gausswid): ln of normalized convolution gauss(*)cauchy" );
     G->register_fct_d_d  ( "lndiehl", func_lndiehl );
 
-    G->register_fct_meta ( "exit", 1, "(x): exit with return value nint(x)" );
-    G->register_fct_i_i  ( "exit", func_exit );
-    G->register_fct_meta ( "exit_if", 1, "(x): exit(0) if x" );
-    G->register_fct_i_i  ( "exit_if", func_exit_if );
-    G->register_fct_meta ( "exit_unless", 1, "(x): exit(0) if !x" );
-    G->register_fct_i_i  ( "exit_unless", func_exit_unless );
-
     // f(2 args)
+    G->register_fct_meta ( "throw_if", 2, "(x,s): throw s if x" );
+    G->register_fct_i_is ( "throw_if", func_throw_if );
+    G->register_fct_meta ( "throw_unless", 1, "(x,s): throw s if !x" );
+    G->register_fct_i_is ( "throw_unless", func_throw_unless );
     G->register_fct_meta ( "min2", 2, "(x,y): the smaller of the two arguments x and y" );
     G->register_fct_i_ii ( "min2", func_min );
     G->register_fct_d_dd ( "min2", func_min );
diff --git a/pub/lib/fregistry.hpp b/pub/lib/fregistry.hpp
index 50f37d7f51e5c9afc6459323dea82d165683ceee..392ceca5cb4ba8121b53e4aba1b0d04f72008bdc 100644
--- a/pub/lib/fregistry.hpp
+++ b/pub/lib/fregistry.hpp
@@ -24,6 +24,7 @@ typedef string (*func_s_s) (string);
 
 typedef int    (*func_i_ii) (int, int);
 typedef int    (*func_i_id) (int, double);
+typedef int    (*func_i_is) (int, string);
 typedef int    (*func_i_di) (double, int);
 typedef int    (*func_i_dd) (double, double);
 typedef int    (*func_i_si) (string, int);
@@ -110,6 +111,9 @@ public:
     void register_fct_i_id( const char* _tag, func_i_id _f )
         { register_fct_template( _tag, "i", "id", (funcPtr)_f ); }
 
+    void register_fct_i_is( const char* _tag, func_i_is _f )
+        { register_fct_template( _tag, "i", "is", (funcPtr)_f ); }
+
     void register_fct_i_di( const char* _tag, func_i_di _f )
         { register_fct_template( _tag, "i", "di", (funcPtr)_f ); }
 
diff --git a/pub/lib/node.cpp b/pub/lib/node.cpp
index 10b450d8f886c18384a954f75fb61d6d1c034dd3..7d511c5ce8d130e0dad19850467efa33f33447b2 100644
--- a/pub/lib/node.cpp
+++ b/pub/lib/node.cpp
@@ -136,6 +136,8 @@ const RObj CNodeFun::tree_val( const CContext& ctx ) const
                     val = (*(func_i_s)(f))( pa[0]->to_s() );
                 else if  ( tf->intypes=="ii" )
                     val = (*(func_i_ii)(f))( pa[0]->to_i(), pa[1]->to_i() );
+                else if  ( tf->intypes=="is" )
+                    val = (*(func_i_is)(f))( pa[0]->to_i(), pa[1]->to_s() );
                 else if  ( tf->intypes=="dd" )
                     val = (*(func_i_dd)(f))( pa[0]->to_r(), pa[1]->to_r() );
                 else if  ( tf->intypes=="si" )
diff --git a/pub/lib/toplevel.cpp b/pub/lib/toplevel.cpp
index 101fee201727f4bdf6f7ffef52b1148034b5c0f8..631dcf66c9a0e72667329d0d4e53e9adbf46e331 100644
--- a/pub/lib/toplevel.cpp
+++ b/pub/lib/toplevel.cpp
@@ -179,16 +179,20 @@ void CFrida::execute_file( const string fnam )
     try{
         cout << "executing " << fnam << "\n";
         ifstream F( fnam );
-        for( lineno=1; ; ++lineno ){
+        for( lineno=0; ; ++lineno ){
             string cmdline = NMacro::readln( "", &F );
             if ( cmdline=="EOF" )
                 break;
-            execute_cmd( cmdline );
+            try {
+                execute_cmd( cmdline );
+            } catch( string& ex ) {
+                throw "'" + cmdline + "': " + ex;
+            }
         }
     } catch( string& ex ) {
         cerr << "Error in script " << fnam << ", line " << lineno << ": " << ex << endl;
     } catch( const char* ex ) {
-        cerr << "Error in script " << fnam << ", line " << lineno << ": " << ex << endl;
+        cerr << "BUG: char* error in script " << fnam << ", line " << lineno << ": " << ex << endl;
     } catch( ... ) {
         cerr << "BUG: catched invalid exception\n";
     }
diff --git a/pub/readplus/macro.cpp b/pub/readplus/macro.cpp
index fae2baa85fe50e7d366fb6d29b6d6ce96471efef..96603bf5e3e8bae5cdf3043d3308f5bdcd21f7c9 100644
--- a/pub/readplus/macro.cpp
+++ b/pub/readplus/macro.cpp
@@ -30,7 +30,6 @@ namespace NMacro {
 
     // the following are only called indirectly through macros:
     void metacmd_incl( string r );
-    string metacmd_echo( string r );
 
     // auxiliary:
     string wordexp_cpp_unique( const string& s );
@@ -196,4 +195,4 @@ void NMacro::metacmd_incl( string r )
 bool NMacro::is_separator( char c )
 {
     return string(" \t").find_first_of( c )!=string::npos;
-}
\ No newline at end of file
+}
diff --git a/pub/test/arithmetics.f2t b/pub/test/arithmetics.f2t
index c382584625900df733457bd19eb0c11fdc493e18..6e050946b12f24f838934d767c583768260ec330 100755
--- a/pub/test/arithmetics.f2t
+++ b/pub/test/arithmetics.f2t
@@ -1,11 +1,11 @@
 #!/usr/bin/env frida
-exit_unless(2+3==5)
-exit_unless(0.2+.3==.5)
-exit_unless(0.2+3==3.2)
-exit_unless(2<3)
-exit_unless(!(3<3))
-exit_unless(3<=3)
-exit_unless(3.1>3)
-exit_unless(!(3.>3))
-exit_unless(!(-3.<-3))
+throw_unless(2+3==5,"arithmetic_failure")
+throw_unless(0.2+.3==.5,"arithmetic_failure")
+throw_unless(0.2+3==3.2,"arithmetic_failure")
+throw_unless(2<3,"arithmetic_failure")
+throw_unless(!(3<3),"arithmetic_failure")
+throw_unless(3<=3,"arithmetic_failure")
+throw_unless(3.1>3,"arithmetic_failure")
+throw_unless(!(3.>3),"arithmetic_failure")
+throw_unless(!(-3.<-3),"arithmetic_failure")
 exit(1)
\ No newline at end of file
diff --git a/pub/test/curve_functionals.f2t b/pub/test/curve_functionals.f2t
index f91c34d0e2040df069501c712785d4e4d6fd939b..900ae2c6ac68eff02bebd983d349f8b1a296a445 100755
--- a/pub/test/curve_functionals.f2t
+++ b/pub/test/curve_functionals.f2t
@@ -1,4 +1,4 @@
 #!/usr/bin/env frida
 cca sin(t)
-exit_unless(abs(integrate(0,pi)-2)<1e-14)
+throw_unless(abs(integrate(0,pi)-2)<1e-14,"integration_failed")
 exit(1)
\ No newline at end of file
diff --git a/pub/test/divisions.f2t b/pub/test/divisions.f2t
index b6154b8f6d211fb9c4a3eef323c5fd60d48a7334..6501107ab54e2a80640a73d9270fe8dfff1bc8a7 100755
--- a/pub/test/divisions.f2t
+++ b/pub/test/divisions.f2t
@@ -1,5 +1,5 @@
 #!/usr/bin/env frida
-exit_unless(abs(5/3-5.0/3.0)<1e-14)
-exit_unless(5//3==1)
-exit_unless(5%3==2)
+throw_unless(abs(5/3-5.0/3.0)<1e-14,"fp_division_failed")
+throw_unless(5//3==1,"int_division_failed")
+throw_unless(5%3==2,"modulo_failed")
 exit(1)
\ No newline at end of file
diff --git a/pub/test/oi_avge.f2t b/pub/test/oi_avge.f2t
index c04454e60679838a62d49f907a31445b7e49842c..a824c2b717b5ef9ea79696fc0e08e9e7376583b4 100755
--- a/pub/test/oi_avge.f2t
+++ b/pub/test/oi_avge.f2t
@@ -1,4 +1,5 @@
 #!/usr/bin/env frida
 fm 11 1 h
 oy i
-exit(avge==5)
+throw_unless(avge==5,"final_result_wrong")
+exit(1)
\ No newline at end of file
diff --git a/pub/test/oi_p.f2t b/pub/test/oi_p.f2t
index 05fd140cb4a61c6589f08b13b7336e1c6e237675..128e95fd86fddbf307c3006c96af6e1824edf984 100755
--- a/pub/test/oi_p.f2t
+++ b/pub/test/oi_p.f2t
@@ -3,4 +3,5 @@ fm 3 3 h
 cc p0*t
 op0 j+.77
 oi p0
-exit(y[,,2]==2.77)
+throw_unless(y[,,2]==2.77,"final_result_wrong")
+exit(1)
\ No newline at end of file
diff --git a/pub/test/oy1.f2t b/pub/test/oy1.f2t
index 35ebc41276cea64a8e3f190d2d1f45aed2bf0462..71b55adc0e81f0d245fc71db8b60ed446f930a5f 100755
--- a/pub/test/oy1.f2t
+++ b/pub/test/oy1.f2t
@@ -4,4 +4,5 @@ ox! i
 oy! j+i
 oz0! j
 oy x+z0
-exit(y[0,2,7]==9&&y[1,2,7]==9)
+throw_unless(y[0,2,7]==9&&y[1,2,7]==9,"final_result_wrong")
+exit(1)
\ No newline at end of file
diff --git a/pub/test/oy_arg_collat.f2t b/pub/test/oy_arg_collat.f2t
index 77eca152c9e3bae6a62bed6ec24c511300f48fdf..23c11be8ba3be1146283589e7bcca23b7f87a7ef 100755
--- a/pub/test/oy_arg_collat.f2t
+++ b/pub/test/oy_arg_collat.f2t
@@ -5,4 +5,5 @@ fm 7 1 h
 oy! i
 0 oy y/y[1,0]
 df
-exit(y[2,2,6]==3)
+throw_unless(y[2,2,6]==3,"final_result_wrong")
+exit(1)
\ No newline at end of file
diff --git a/pub/test/run b/pub/test/run
index 3736c5f46a242e61bb71f0f18df1a612ccb75838..b5abbc4ca7243008e38a27cce665e31f2ef07261 100755
--- a/pub/test/run
+++ b/pub/test/run
@@ -11,4 +11,4 @@ for I in *.f2t; do
        let COUNTER=COUNTER+1
     fi    
 done
-echo "=> $COUNTER failures"
+echo "=> $COUNTER failures. See log.tmp for full dialogs."
diff --git a/pub/test/ternary.f2t b/pub/test/ternary.f2t
index 76d661b06a971c0ac19680ba457fce62cbc1b23a..ca9c42f529241caf61fa6ccd10fa52d8e2ed6416 100755
--- a/pub/test/ternary.f2t
+++ b/pub/test/ternary.f2t
@@ -1,4 +1,4 @@
 #!/usr/bin/env frida
-exit_unless(0?2:3==3)
-exit_unless(1?4:5==4)
+throw_unless(0?2:3==3,"cond_expr_failed")
+throw_unless(1?4:5==4,"cond_expr_failed")
 exit(1)