From 68f08c4bb53fcde16035b622bdb6e9529f9cf3ae Mon Sep 17 00:00:00 2001
From: John Darrington <john@darrington.wattle.id.au>
Date: Wed, 5 Sep 2007 06:23:01 +0000
Subject: [PATCH] Implemented the INSERT command.

---
 doc/language.texi                   |  39 ++++-
 doc/utilities.texi                  |  60 ++++++-
 po/de.po                            |  89 ++++++----
 po/pspp.pot                         |  89 ++++++----
 src/language/ChangeLog              |   7 +
 src/language/command.c              |  43 ++++-
 src/language/command.def            |   1 +
 src/language/control/repeat.c       |  24 +--
 src/language/data-io/data-reader.c  |   2 +-
 src/language/lexer/lexer.c          |  33 ++--
 src/language/lexer/lexer.h          |   8 +-
 src/language/syntax-file.c          |   5 +-
 src/language/syntax-file.h          |  10 +-
 src/language/syntax-string-source.c |   3 +-
 src/language/utilities/include.c    | 159 +++++++++++++++--
 src/libpspp/ChangeLog               |   5 +
 src/libpspp/getl.c                  |  45 ++++-
 src/libpspp/getl.h                  |  35 +++-
 src/ui/gui/helper.c                 |   2 +-
 src/ui/gui/syntax-editor-source.c   |   3 +-
 src/ui/terminal/command-line.c      |  17 +-
 src/ui/terminal/read-line.c         |   3 +-
 tests/automake.mk                   |   1 +
 tests/command/insert.sh             | 253 ++++++++++++++++++++++++++++
 24 files changed, 765 insertions(+), 171 deletions(-)
 create mode 100755 tests/command/insert.sh

diff --git a/doc/language.texi b/doc/language.texi
index 398f764231..1345433613 100644
--- a/doc/language.texi
+++ b/doc/language.texi
@@ -15,6 +15,7 @@ Later chapters will describe individual commands in detail.
 @menu
 * Tokens::                      Characters combine to form tokens.
 * Commands::                    Tokens combine to form commands.
+* Syntax Variants::             Batch vs. Interactive mode
 * Types of Commands::           Commands come in several flavors.
 * Order of Commands::           Commands combine to form syntax files.
 * Missing Observations::        Handling missing observations.
@@ -24,6 +25,7 @@ Later chapters will describe individual commands in detail.
 * BNF::                         How command syntax is described.
 @end menu
 
+
 @node Tokens
 @section Tokens
 @cindex language, lexical analysis
@@ -188,15 +190,34 @@ one that consists only of white space or comments, also ends a command
 by default, although you can use the NULLINE subcommand of @cmd{SET}
 to disable this feature (@pxref{SET}).
 
-In batch mode only, that is, when reading commands from a file instead
-of an interactive user, any line that contains a non-space character
-in the leftmost column begins a new command.  Thus, each command
-consists of a flush-left line followed by any number of lines indented
-from the left margin.  In this mode, a plus or minus sign
-(@samp{+}, @samp{@minus{}}) as the first character
-in a line is ignored and causes that line to begin a new command,
-which allows for visual indentation of a command without that command
-being considered part of the previous command.
+@node Syntax Variants
+@section Variants of syntax.
+
+@cindex Batch syntax
+@cindex Interactive syntax
+
+There are two variants of command syntax, @i{viz}: @dfn{batch} mode and
+@dfn{interactive} mode.
+Batch mode is the default when reading commands from a file.
+Interactive mode is the default when commands are typed at a prompt
+by a user.
+Certain commands, such as @cmd{INSERT} (@pxref{INSERT}), may explicitly
+change the syntax mode. 
+
+In batch mode, any line that contains a non-space character
+in the leftmost column begins a new command. 
+Thus, each command consists of a flush-left line followed by any
+number of lines indented from the left margin. 
+In this mode, a plus or minus sign (@samp{+}, @samp{@minus{}}) as the
+first character in a line is ignored and causes that line to begin a
+new command, which allows for visual indentation of a command without
+that command being considered part of the previous command. 
+The period terminating the end of a command is optional but recommended.
+
+In interactive mode, each command must  either be terminated with a period,
+or an empty line must follow the command.
+The use of (@samp{+} and @samp{@minus{}} as continuation characters is not
+permitted.
 
 @node Types of Commands
 @section Types of Commands
diff --git a/doc/utilities.texi b/doc/utilities.texi
index ba5a5e8b09..94fa50744d 100644
--- a/doc/utilities.texi
+++ b/doc/utilities.texi
@@ -23,6 +23,7 @@ encountered in the input.
 * FINISH::                      Terminate the PSPP session.
 * HOST::                        Temporarily return to the operating system.
 * INCLUDE::                     Include a file within the current one.
+* INSERT::                      Insert a file within the current one.
 * PERMISSIONS::                 Change permissions on a file.
 * SET::                         Adjust PSPP runtime parameters.
 * SHOW::                        Display runtime parameters.
@@ -220,23 +221,70 @@ This command cannot be used if the SAFER setting is active.
 @node INCLUDE
 @section INCLUDE
 @vindex INCLUDE
-@vindex @@
 
 @display
-Two possible syntaxes:
-        INCLUDE 'file-name'.
-        @@file-name.
+        INCLUDE [FILE=]'file-name'.
 @end display
 
 @cmd{INCLUDE} causes the PSPP command processor to read an
 additional command file as if it were included bodily in the current
 command file.
-
+If errors are encountered in the included file, then command processing will 
+stop and no more commands will be processed.
 Include files may be nested to any depth, up to the limit of available
 memory.
 
+
+The @cmd{INSERT} command (@pxref{INSERT}) may be used instead of
+@cmd{INCLUDE} if you require more flexible options.
+The syntax 
+@example
+INCLUDE FILE=@var{file-name}.
+@end example
+@noindent 
+functions identically to 
+@example
+INSERT FILE=@var{file-name} ERROR=STOP CD=NO SYNTAX=BATCH.
+@end example
+
+
+@node INSERT
+@section INSERT
+@vindex INSERT
+
+@display
+     INSERT [FILE=]'file-name'
+        [CD=@{NO,YES@}]
+        [ERROR=@{CONTINUE,STOP@}]
+        [SYNTAX=@{BATCH,INTERACTIVE@}].
+@end display
+
+@cmd{INSERT} is similar to @cmd{INCLUDE} (@pxref{INCLUDE}) 
+but somewhat more flexible.
+It causes the command processor to read a file as if it were embedded in the 
+current command file.
+
+If @samp{CD=YES} is specified, then before including the file, the
+current directory  will be changed to the directory of the included
+file.  
+The default setting is @samp{CD=NO}.
+Note that this directory will remain current until it is
+changed explicitly (with the @cmd{CD} command, or a subsequent
+@cmd{INSERT} command with the @samp{CD=YES} option).
+It will not revert to its original setting even after the included
+file is finished processing.
+
+If @samp{ERROR=STOP} is specified, errors encountered in the
+inserted file will cause processing to immediately cease.
+Otherwise processing will continue at the next command.
+The default setting is @samp{ERROR=CONTINUE}.
+
+If @samp{SYNTAX=INTERACTIVE} is specified then the syntax contained in
+the included file must conform to interactive syntax
+conventions. @xref{Syntax Variants}.
+The default setting is @samp{SYNTAX=BATCH}.
+
 @node PERMISSIONS
-@comment  node-name,  next,  previous,  up
 @section PERMISSIONS
 @vindex PERMISSIONS
 @cindex mode
diff --git a/po/de.po b/po/de.po
index c92f367f24..cad53f8a70 100644
--- a/po/de.po
+++ b/po/de.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PSPP 0.4.3\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2007-08-31 07:06+0800\n"
+"POT-Creation-Date: 2007-09-01 08:59+0800\n"
 "PO-Revision-Date: 2006-07-28 19:32+0800\n"
 "Last-Translator: John Darrington <john@darrington.wattle.id.au>\n"
 "Language-Team: German <pspp-dev@gnu.org>\n"
@@ -988,41 +988,41 @@ msgstr ""
 msgid "Only one index clause may be specified."
 msgstr ""
 
-#: src/language/control/repeat.c:168
+#: src/language/control/repeat.c:173
 #, c-format
 msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
 msgstr ""
 
-#: src/language/control/repeat.c:173
+#: src/language/control/repeat.c:178
 #, c-format
 msgid "Dummy variable name \"%s\" is given twice."
 msgstr ""
 
-#: src/language/control/repeat.c:219
+#: src/language/control/repeat.c:224
 #, c-format
 msgid ""
 "Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
 "were specified."
 msgstr ""
 
-#: src/language/control/repeat.c:331
+#: src/language/control/repeat.c:336
 msgid "DO REPEAT may not nest in compatibility mode."
 msgstr ""
 
-#: src/language/control/repeat.c:433
+#: src/language/control/repeat.c:438
 msgid "Ranges may only have integer bounds"
 msgstr ""
 
-#: src/language/control/repeat.c:442
+#: src/language/control/repeat.c:447
 #, c-format
 msgid "%g TO %g is an invalid range."
 msgstr ""
 
-#: src/language/control/repeat.c:477
+#: src/language/control/repeat.c:482
 msgid "String expected."
 msgstr ""
 
-#: src/language/control/repeat.c:496
+#: src/language/control/repeat.c:501
 msgid "No matching DO REPEAT."
 msgstr ""
 
@@ -1411,7 +1411,7 @@ msgid_plural "Writing %d records."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:464
+#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:471
 #: src/language/stats/autorecode.c:154 src/language/xforms/select-if.c:59
 msgid "expecting end of command"
 msgstr ""
@@ -1828,11 +1828,11 @@ msgid ""
 "s."
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:608
+#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:615
 msgid "expecting string"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:622
+#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:629
 msgid "expecting integer"
 msgstr ""
 
@@ -2079,89 +2079,89 @@ msgstr ""
 msgid "expecting format type"
 msgstr ""
 
-#: src/language/lexer/lexer.c:270
+#: src/language/lexer/lexer.c:277
 #, c-format
 msgid "%s does not form a valid number."
 msgstr ""
 
-#: src/language/lexer/lexer.c:374
+#: src/language/lexer/lexer.c:381
 #, c-format
 msgid "Bad character in input: `%c'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:376
+#: src/language/lexer/lexer.c:383
 #, c-format
 msgid "Bad character in input: `\\%o'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:412
+#: src/language/lexer/lexer.c:419
 #, c-format
 msgid "Subcommand %s may only be specified once."
 msgstr ""
 
-#: src/language/lexer/lexer.c:420
+#: src/language/lexer/lexer.c:427
 #, c-format
 msgid "missing required subcommand %s"
 msgstr ""
 
-#: src/language/lexer/lexer.c:449
+#: src/language/lexer/lexer.c:456
 #, c-format
 msgid "Syntax error %s at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:452
+#: src/language/lexer/lexer.c:459
 #, c-format
 msgid "Syntax error at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:577 src/language/lexer/lexer.c:594
+#: src/language/lexer/lexer.c:584 src/language/lexer/lexer.c:601
 #, c-format
 msgid "expecting `%s'"
 msgstr ""
 
-#: src/language/lexer/lexer.c:635
+#: src/language/lexer/lexer.c:642
 msgid "expecting number"
 msgstr ""
 
-#: src/language/lexer/lexer.c:647
+#: src/language/lexer/lexer.c:654
 msgid "expecting identifier"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1045
+#: src/language/lexer/lexer.c:1048
 msgid "binary"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1050
+#: src/language/lexer/lexer.c:1053
 msgid "octal"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1055
+#: src/language/lexer/lexer.c:1058
 msgid "hex"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1065
+#: src/language/lexer/lexer.c:1068
 #, c-format
 msgid "String of %s digits has %d characters, which is not a multiple of %d."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1094
+#: src/language/lexer/lexer.c:1097
 #, c-format
 msgid "`%c' is not a valid %s digit."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1128
+#: src/language/lexer/lexer.c:1131
 msgid "Unterminated string constant."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1182
+#: src/language/lexer/lexer.c:1185
 msgid "Unexpected end of file in string concatenation."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1190
+#: src/language/lexer/lexer.c:1193
 msgid "String expected following `+'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1203
+#: src/language/lexer/lexer.c:1206
 #, c-format
 msgid "String exceeds 255 characters in length (%d characters)."
 msgstr ""
@@ -3494,7 +3494,7 @@ msgstr ""
 msgid "Reading `%s': %s."
 msgstr ""
 
-#: src/language/syntax-file.c:125
+#: src/language/syntax-file.c:124
 #, c-format
 msgid "Closing `%s': %s."
 msgstr ""
@@ -3550,15 +3550,32 @@ msgstr ""
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
-#: src/language/utilities/include.c:47
+#: src/language/utilities/include.c:47 src/language/utilities/insert.c:138
 msgid "expecting file name"
 msgstr ""
 
-#: src/language/utilities/include.c:62
+#: src/language/utilities/include.c:63 src/language/utilities/insert.c:149
 #, c-format
 msgid "Can't find `%s' in include file search path."
 msgstr ""
 
+#: src/language/utilities/insert.c:60
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
+msgstr ""
+
+#: src/language/utilities/insert.c:77
+msgid "Expecting YES or NO after CD."
+msgstr ""
+
+#: src/language/utilities/insert.c:94
+msgid "Expecting CONTINUE or STOP after ERROR."
+msgstr ""
+
+#: src/language/utilities/insert.c:101
+#, fuzzy, c-format
+msgid "Unexpected token: `%s'."
+msgstr "plotzlich ist der Datei beendet"
+
 #: src/language/utilities/permissions.c:73
 #, c-format
 msgid "Expecting %s or %s."
@@ -5280,7 +5297,7 @@ msgstr ""
 msgid "%s --- PSPP Output"
 msgstr "PSPP Dateiaufbereiter"
 
-#: src/ui/terminal/command-line.c:219
+#: src/ui/terminal/command-line.c:223
 #, c-format
 msgid ""
 "PSPP, a program for statistical analysis of sample data.\n"
@@ -5326,7 +5343,7 @@ msgid ""
 "\n"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:254
+#: src/ui/terminal/command-line.c:258
 #, c-format
 msgid ""
 "\n"
diff --git a/po/pspp.pot b/po/pspp.pot
index 5123ff96ef..148a959c78 100644
--- a/po/pspp.pot
+++ b/po/pspp.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2007-08-31 07:06+0800\n"
+"POT-Creation-Date: 2007-09-01 08:59+0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -982,41 +982,41 @@ msgstr ""
 msgid "Only one index clause may be specified."
 msgstr ""
 
-#: src/language/control/repeat.c:168
+#: src/language/control/repeat.c:173
 #, c-format
 msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
 msgstr ""
 
-#: src/language/control/repeat.c:173
+#: src/language/control/repeat.c:178
 #, c-format
 msgid "Dummy variable name \"%s\" is given twice."
 msgstr ""
 
-#: src/language/control/repeat.c:219
+#: src/language/control/repeat.c:224
 #, c-format
 msgid ""
 "Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
 "were specified."
 msgstr ""
 
-#: src/language/control/repeat.c:331
+#: src/language/control/repeat.c:336
 msgid "DO REPEAT may not nest in compatibility mode."
 msgstr ""
 
-#: src/language/control/repeat.c:433
+#: src/language/control/repeat.c:438
 msgid "Ranges may only have integer bounds"
 msgstr ""
 
-#: src/language/control/repeat.c:442
+#: src/language/control/repeat.c:447
 #, c-format
 msgid "%g TO %g is an invalid range."
 msgstr ""
 
-#: src/language/control/repeat.c:477
+#: src/language/control/repeat.c:482
 msgid "String expected."
 msgstr ""
 
-#: src/language/control/repeat.c:496
+#: src/language/control/repeat.c:501
 msgid "No matching DO REPEAT."
 msgstr ""
 
@@ -1405,7 +1405,7 @@ msgid_plural "Writing %d records."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:464
+#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:471
 #: src/language/stats/autorecode.c:154 src/language/xforms/select-if.c:59
 msgid "expecting end of command"
 msgstr ""
@@ -1822,11 +1822,11 @@ msgid ""
 "s."
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:608
+#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:615
 msgid "expecting string"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:622
+#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:629
 msgid "expecting integer"
 msgstr ""
 
@@ -2073,89 +2073,89 @@ msgstr ""
 msgid "expecting format type"
 msgstr ""
 
-#: src/language/lexer/lexer.c:270
+#: src/language/lexer/lexer.c:277
 #, c-format
 msgid "%s does not form a valid number."
 msgstr ""
 
-#: src/language/lexer/lexer.c:374
+#: src/language/lexer/lexer.c:381
 #, c-format
 msgid "Bad character in input: `%c'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:376
+#: src/language/lexer/lexer.c:383
 #, c-format
 msgid "Bad character in input: `\\%o'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:412
+#: src/language/lexer/lexer.c:419
 #, c-format
 msgid "Subcommand %s may only be specified once."
 msgstr ""
 
-#: src/language/lexer/lexer.c:420
+#: src/language/lexer/lexer.c:427
 #, c-format
 msgid "missing required subcommand %s"
 msgstr ""
 
-#: src/language/lexer/lexer.c:449
+#: src/language/lexer/lexer.c:456
 #, c-format
 msgid "Syntax error %s at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:452
+#: src/language/lexer/lexer.c:459
 #, c-format
 msgid "Syntax error at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:577 src/language/lexer/lexer.c:594
+#: src/language/lexer/lexer.c:584 src/language/lexer/lexer.c:601
 #, c-format
 msgid "expecting `%s'"
 msgstr ""
 
-#: src/language/lexer/lexer.c:635
+#: src/language/lexer/lexer.c:642
 msgid "expecting number"
 msgstr ""
 
-#: src/language/lexer/lexer.c:647
+#: src/language/lexer/lexer.c:654
 msgid "expecting identifier"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1045
+#: src/language/lexer/lexer.c:1048
 msgid "binary"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1050
+#: src/language/lexer/lexer.c:1053
 msgid "octal"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1055
+#: src/language/lexer/lexer.c:1058
 msgid "hex"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1065
+#: src/language/lexer/lexer.c:1068
 #, c-format
 msgid "String of %s digits has %d characters, which is not a multiple of %d."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1094
+#: src/language/lexer/lexer.c:1097
 #, c-format
 msgid "`%c' is not a valid %s digit."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1128
+#: src/language/lexer/lexer.c:1131
 msgid "Unterminated string constant."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1182
+#: src/language/lexer/lexer.c:1185
 msgid "Unexpected end of file in string concatenation."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1190
+#: src/language/lexer/lexer.c:1193
 msgid "String expected following `+'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1203
+#: src/language/lexer/lexer.c:1206
 #, c-format
 msgid "String exceeds 255 characters in length (%d characters)."
 msgstr ""
@@ -3487,7 +3487,7 @@ msgstr ""
 msgid "Reading `%s': %s."
 msgstr ""
 
-#: src/language/syntax-file.c:125
+#: src/language/syntax-file.c:124
 #, c-format
 msgid "Closing `%s': %s."
 msgstr ""
@@ -3543,15 +3543,32 @@ msgstr ""
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
-#: src/language/utilities/include.c:47
+#: src/language/utilities/include.c:47 src/language/utilities/insert.c:138
 msgid "expecting file name"
 msgstr ""
 
-#: src/language/utilities/include.c:62
+#: src/language/utilities/include.c:63 src/language/utilities/insert.c:149
 #, c-format
 msgid "Can't find `%s' in include file search path."
 msgstr ""
 
+#: src/language/utilities/insert.c:60
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
+msgstr ""
+
+#: src/language/utilities/insert.c:77
+msgid "Expecting YES or NO after CD."
+msgstr ""
+
+#: src/language/utilities/insert.c:94
+msgid "Expecting CONTINUE or STOP after ERROR."
+msgstr ""
+
+#: src/language/utilities/insert.c:101
+#, c-format
+msgid "Unexpected token: `%s'."
+msgstr ""
+
 #: src/language/utilities/permissions.c:73
 #, c-format
 msgid "Expecting %s or %s."
@@ -5225,7 +5242,7 @@ msgstr ""
 msgid "%s --- PSPP Output"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:219
+#: src/ui/terminal/command-line.c:223
 #, c-format
 msgid ""
 "PSPP, a program for statistical analysis of sample data.\n"
@@ -5271,7 +5288,7 @@ msgid ""
 "\n"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:254
+#: src/ui/terminal/command-line.c:258
 #, c-format
 msgid ""
 "\n"
diff --git a/src/language/ChangeLog b/src/language/ChangeLog
index 8949ff75c1..d25c2d0379 100644
--- a/src/language/ChangeLog
+++ b/src/language/ChangeLog
@@ -1,3 +1,10 @@
+2007-09-05  John Darrington <john@darrington.wattle.id.au>
+
+	* command.c (do_parse_command): Translate CMD_FAILURE into
+	CMD_CASCADING_FAILURE, if the ERRMODE_STOP is set on the syntax
+	source. 
+	
+	
 2007-06-06  Ben Pfaff  <blp@gnu.org>
 
 	* command.def: Add DEBUG DATASHEET command.  Remove DEBUG CASEFILE
diff --git a/src/language/command.c b/src/language/command.c
index 40b8505eba..1688a10eb6 100644
--- a/src/language/command.c
+++ b/src/language/command.c
@@ -39,6 +39,7 @@
 #include <libpspp/str.h>
 #include <output/manager.h>
 #include <output/table.h>
+#include <libpspp/getl.h>
 
 #if HAVE_SYS_WAIT_H
 #include <sys/wait.h>
@@ -169,7 +170,8 @@ cmd_parse (struct lexer *lexer, struct dataset *ds)
 /* Parses an entire command, from command name to terminating
    dot. */
 static enum cmd_result
-do_parse_command (struct lexer *lexer, struct dataset *ds, enum cmd_state state)
+do_parse_command (struct lexer *lexer,
+		  struct dataset *ds, enum cmd_state state)
 {
   const struct command *command;
   enum cmd_result result;
@@ -179,38 +181,50 @@ do_parse_command (struct lexer *lexer, struct dataset *ds, enum cmd_state state)
   set_completion_state (state);
   lex_get (lexer);
   if (lex_token (lexer) == T_STOP)
-    return CMD_EOF;
+    {
+      result = CMD_EOF;
+      goto finish;
+    }
   else if (lex_token (lexer) == '.')
     {
       /* Null commands can result from extra empty lines. */
-      return CMD_SUCCESS;
+      result = CMD_SUCCESS;
+      goto finish;
     }
+
   prompt_set_style (PROMPT_LATER);
 
   /* Parse the command name. */
   command = parse_command_name (lexer);
   if (command == NULL)
-    return CMD_FAILURE;
+    {
+      result = CMD_FAILURE;
+      goto finish;
+    }
   else if (command->function == NULL)
     {
       msg (SE, _("%s is unimplemented."), command->name);
-      return CMD_NOT_IMPLEMENTED;
+      result = CMD_NOT_IMPLEMENTED;
+      goto finish;
     }
   else if ((command->flags & F_TESTING) && !get_testing_mode ())
     {
       msg (SE, _("%s may be used only in testing mode."), command->name);
-      return CMD_FAILURE;
+      result = CMD_FAILURE;
+      goto finish;
     }
   else if ((command->flags & F_ENHANCED) && get_syntax () != ENHANCED)
     {
       msg (SE, _("%s may be used only in enhanced syntax mode."),
            command->name);
-      return CMD_FAILURE;
+      result = CMD_FAILURE;
+      goto finish;
     }
   else if (!in_correct_state (command, state))
     {
       report_state_mismatch (command, state);
-      return CMD_FAILURE;
+      result = CMD_FAILURE;
+      goto finish;
     }
 
   /* Execute command. */
@@ -221,6 +235,19 @@ do_parse_command (struct lexer *lexer, struct dataset *ds, enum cmd_state state)
   msg_set_command_name (NULL);
 
   assert (cmd_result_is_valid (result));
+
+ finish:
+  if ( cmd_result_is_failure (result))
+    {
+      const struct source_stream *cs = lex_get_source_stream (lexer);
+
+      if ( source_stream_current_error_mode (cs) == ERRMODE_STOP )
+	{
+	  msg (MW, _("Error encountered while ERROR=STOP is effective."));
+	  result = CMD_CASCADING_FAILURE;
+	}
+    }
+
   return result;
 }
 
diff --git a/src/language/command.def b/src/language/command.def
index 5967e5d289..7a89ae0a16 100644
--- a/src/language/command.def
+++ b/src/language/command.def
@@ -26,6 +26,7 @@ DEF_CMD (S_ANY, F_KEEP_FINAL_TOKEN, "FILE LABEL", cmd_file_label)
 DEF_CMD (S_ANY, 0, "FINISH", cmd_finish)
 DEF_CMD (S_ANY, F_KEEP_FINAL_TOKEN, "HOST", cmd_host)
 DEF_CMD (S_ANY, 0, "INCLUDE", cmd_include)
+DEF_CMD (S_ANY, 0, "INSERT", cmd_insert)
 DEF_CMD (S_ANY, 0, "N OF CASES", cmd_n_of_cases)
 DEF_CMD (S_ANY, F_ABBREV, "N", cmd_n_of_cases)
 DEF_CMD (S_ANY, 0, "NEW FILE", cmd_new_file)
diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c
index ede37016ee..cd56d7c811 100644
--- a/src/language/control/repeat.c
+++ b/src/language/control/repeat.c
@@ -49,7 +49,6 @@ struct repeat_line
     const char *file_name;      /* File name. */
     int line_number;            /* Line number. */
     struct substring text;	/* Contents. */
-    enum getl_syntax syntax;    /* Syntax mode. */
   };
 
 /* The type of substitution made for a DO REPEAT macro. */
@@ -103,9 +102,9 @@ static int parse_strings (struct lexer *, struct repeat_macro *,
 			  struct pool *);
 
 static void do_repeat_filter (struct getl_interface *,
-                              struct string *, enum getl_syntax);
+                              struct string *);
 static bool do_repeat_read (struct getl_interface *,
-                            struct string *, enum getl_syntax *);
+                            struct string *);
 static void do_repeat_close (struct getl_interface *);
 static bool always_false (const struct getl_interface *);
 static const char *do_repeat_name (const struct getl_interface *);
@@ -136,7 +135,11 @@ cmd_do_repeat (struct lexer *lexer, struct dataset *ds)
   block->parent.location = do_repeat_location;
 
   if (!ll_is_empty (&block->lines))
-    getl_include_source (lex_get_source_stream (lexer), &block->parent);
+    getl_include_source (lex_get_source_stream (lexer),
+			 &block->parent,
+			 lex_current_syntax_mode (lexer),
+			 lex_current_error_mode (lexer)
+			 );
   else
     pool_destroy (block->pool);
 
@@ -298,11 +301,10 @@ parse_lines (struct lexer *lexer, struct repeat_block *block)
       const char *cur_file_name;
       struct repeat_line *line;
       struct string text;
-      enum getl_syntax syntax;
       bool command_ends_before_line, command_ends_after_line;
 
       /* Retrieve an input line and make a copy of it. */
-      if (!lex_get_line_raw (lexer, &syntax))
+      if (!lex_get_line_raw (lexer))
         return false;
       ds_init_string (&text, lex_entire_line_ds (lexer));
 
@@ -318,11 +320,12 @@ parse_lines (struct lexer *lexer, struct repeat_block *block)
       line->file_name = previous_file_name;
       line->line_number = getl_source_location (lex_get_source_stream (lexer));
       ss_alloc_substring_pool (&line->text, ds_ss (&text), block->pool);
-      line->syntax = syntax;
+
 
       /* Check whether the line contains a DO REPEAT or END
          REPEAT command. */
-      lex_preprocess_line (&text, syntax,
+      lex_preprocess_line (&text,
+			   lex_current_syntax_mode (lexer),
                            &command_ends_before_line,
                            &command_ends_after_line);
       if (recognize_do_repeat (ds_ss (&text)))
@@ -510,7 +513,7 @@ find_substitution (struct repeat_block *block, struct substring name)
    repeated lines. */
 static void
 do_repeat_filter (struct getl_interface *block_,
-                  struct string *line, enum getl_syntax syntax UNUSED)
+                  struct string *line)
 {
   struct repeat_block *block = (struct repeat_block *) block_;
   bool in_apos, in_quote, dot;
@@ -566,7 +569,7 @@ current_line (const struct getl_interface *interface)
    was obtained, false if the source is exhausted. */
 static bool
 do_repeat_read  (struct getl_interface *interface,
-                 struct string *output, enum getl_syntax *syntax)
+                 struct string *output)
 {
   struct repeat_block *block = (struct repeat_block *) interface;
   struct repeat_line *line;
@@ -583,7 +586,6 @@ do_repeat_read  (struct getl_interface *interface,
 
   line = current_line (interface);
   ds_assign_substring (output, line->text);
-  *syntax = line->syntax;
   return true;
 }
 
diff --git a/src/language/data-io/data-reader.c b/src/language/data-io/data-reader.c
index c752587b5b..7176aed43a 100644
--- a/src/language/data-io/data-reader.c
+++ b/src/language/data-io/data-reader.c
@@ -170,7 +170,7 @@ read_inline_record (struct dfm_reader *r)
       prompt_set_style (PROMPT_DATA);
     }
 
-  if (!lex_get_line_raw (r->lexer, NULL))
+  if (!lex_get_line_raw (r->lexer))
     {
       msg (SE, _("Unexpected end-of-file while reading data in BEGIN "
                  "DATA.  This probably indicates "
diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c
index 4e10ec2247..7159730203 100644
--- a/src/language/lexer/lexer.c
+++ b/src/language/lexer/lexer.c
@@ -43,6 +43,7 @@
 #define DUMP_TOKENS 0
 
 
+
 struct lexer
 {
   struct string line_buffer;
@@ -108,6 +109,18 @@ lex_get_source_stream (const struct lexer *lex)
   return lex->ss;
 }
 
+enum syntax_mode
+lex_current_syntax_mode (const struct lexer *lex)
+{
+  return source_stream_current_syntax_mode (lex->ss);
+}
+
+enum error_mode
+lex_current_error_mode (const struct lexer *lex)
+{
+  return source_stream_current_error_mode (lex->ss);
+}
+
 
 void
 lex_destroy (struct lexer *lexer)
@@ -832,7 +845,7 @@ strip_comments (struct string *string)
    *LINE_STARTS_COMMAND and *LINE_ENDS_COMMAND appropriately. */
 void
 lex_preprocess_line (struct string *line,
-                     enum getl_syntax syntax,
+                     enum syntax_mode syntax,
                      bool *line_starts_command,
                      bool *line_ends_command)
 {
@@ -854,15 +867,11 @@ lex_preprocess_line (struct string *line,
    Sets *SYNTAX, if SYNTAX is non-null, to the line's syntax
    mode. */
 bool
-lex_get_line_raw (struct lexer *lexer, enum getl_syntax *syntax)
+lex_get_line_raw (struct lexer *lexer)
 {
-  enum getl_syntax dummy;
-  bool ok;
-
-  if (syntax == NULL)
-    syntax = &dummy;
-  ok = getl_read_line (lexer->ss, &lexer->line_buffer, syntax);
-  journal_write (*syntax == GETL_BATCH, ds_cstr (&lexer->line_buffer));
+  bool ok = getl_read_line (lexer->ss, &lexer->line_buffer);
+  enum syntax_mode mode = lex_current_syntax_mode (lexer);
+  journal_write (mode == GETL_BATCH, ds_cstr (&lexer->line_buffer));
 
   return ok;
 }
@@ -874,15 +883,15 @@ bool
 lex_get_line (struct lexer *lexer)
 {
   bool line_starts_command;
-  enum getl_syntax syntax = GETL_BATCH;
 
-  if (!lex_get_line_raw (lexer, &syntax))
+  if (!lex_get_line_raw (lexer))
     {
       lexer->prog = NULL;
       return false;
     }
 
-  lex_preprocess_line (&lexer->line_buffer, syntax,
+  lex_preprocess_line (&lexer->line_buffer,
+		       lex_current_syntax_mode (lexer),
                        &line_starts_command, &lexer->dot);
 
   if (line_starts_command)
diff --git a/src/language/lexer/lexer.h b/src/language/lexer/lexer.h
index 44be0e930d..ada09ade9a 100644
--- a/src/language/lexer/lexer.h
+++ b/src/language/lexer/lexer.h
@@ -30,8 +30,10 @@ struct lexer;
 struct lexer * lex_create (struct source_stream *);
 void lex_destroy (struct lexer *);
 
+/* State accessors */
 struct source_stream * lex_get_source_stream (const struct lexer *);
-
+enum syntax_mode lex_current_syntax_mode (const struct lexer *);
+enum error_mode lex_current_error_mode (const struct lexer *);
 
 /* Common functions. */
 void lex_get (struct lexer *);
@@ -71,7 +73,7 @@ const char *lex_entire_line (const struct lexer *);
 const struct string *lex_entire_line_ds (const struct lexer *);
 const char *lex_rest_of_line (const struct lexer *);
 bool lex_end_dot (const struct lexer *);
-void lex_preprocess_line (struct string *, enum getl_syntax,
+void lex_preprocess_line (struct string *, enum syntax_mode,
                           bool *line_starts_command,
                           bool *line_ends_command);
 void lex_discard_line (struct lexer *);
@@ -79,7 +81,7 @@ void lex_discard_rest_of_command (struct lexer *);
 
 /* Weird line reading functions. */
 bool lex_get_line (struct lexer *);
-bool lex_get_line_raw (struct lexer *, enum getl_syntax *);
+bool lex_get_line_raw (struct lexer *);
 
 /* Token names. */
 const char *lex_token_name (int);
diff --git a/src/language/syntax-file.c b/src/language/syntax-file.c
index b3ae6f4aca..dde807c37c 100644
--- a/src/language/syntax-file.c
+++ b/src/language/syntax-file.c
@@ -74,9 +74,9 @@ line_number (const struct getl_interface *s)
 
 /* Reads a line from syntax file source S into LINE.
    Returns true if successful, false at end of file. */
-bool
+static bool
 read_syntax_file (struct getl_interface *s,
-                  struct string *line, enum getl_syntax *syntax)
+                  struct string *line)
 {
   struct syntax_file_source *sfs = (struct syntax_file_source *) s;
 
@@ -112,7 +112,6 @@ read_syntax_file (struct getl_interface *s,
   if (get_echo ())
     tab_output_text (TAB_LEFT | TAB_FIX, ds_cstr (line));
 
-  *syntax = GETL_BATCH;
   return true;
 }
 
diff --git a/src/language/syntax-file.h b/src/language/syntax-file.h
index c038a4327a..8044f3c04e 100644
--- a/src/language/syntax-file.h
+++ b/src/language/syntax-file.h
@@ -17,15 +17,9 @@
 #if !SYNTAX_FILE
 #define SYNTAX_FILE 1
 
-#include <stdbool.h>
-#include <libpspp/getl.h>
-
-struct string;
-
-bool read_syntax_file (struct getl_interface *s,
-                       struct string *line, enum getl_syntax *syntax);
+struct getl_interface;
 
 /* Creates a syntax file source with file name FN. */
-struct getl_interface * create_syntax_file_source (const char *fn) ;
+struct getl_interface * create_syntax_file_source (const char *) ;
 
 #endif
diff --git a/src/language/syntax-string-source.c b/src/language/syntax-string-source.c
index bcdcbd3767..f80bca2e9e 100644
--- a/src/language/syntax-string-source.c
+++ b/src/language/syntax-string-source.c
@@ -70,8 +70,7 @@ do_close (struct getl_interface *i )
 
 static bool
 read_single_line (struct getl_interface *i,
-		  struct string *line,
-		  enum getl_syntax *syntax_rules UNUSED)
+		  struct string *line)
 {
   struct syntax_string_source *sss = (struct syntax_string_source *) i;
 
diff --git a/src/language/utilities/include.c b/src/language/utilities/include.c
index 0ef9d463d7..26c718a42d 100644
--- a/src/language/utilities/include.c
+++ b/src/language/utilities/include.c
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -25,17 +25,147 @@
 #include <language/lexer/lexer.h>
 #include <libpspp/str.h>
 #include <data/file-name.h>
+#include <dirname.h>
+#include <canonicalize.h>
 
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+static int parse_insert (struct lexer *lexer, char **filename);
+
+
 int
 cmd_include (struct lexer *lexer, struct dataset *ds UNUSED)
 {
-  struct source_stream *ss;
-  char *found_fn;
+  char *filename = NULL;
+  int status = parse_insert (lexer, &filename);
+
+  if ( CMD_SUCCESS != status)
+    return status;
+
+  lex_get (lexer);
+
+  status = lex_end_of_command (lexer);
+
+  if ( status == CMD_SUCCESS)
+    {
+      struct source_stream *ss = lex_get_source_stream (lexer);
+
+      assert (filename);
+      getl_include_source (ss, create_syntax_file_source (filename),
+			   GETL_BATCH, ERRMODE_STOP);
+      free (filename);
+    }
+
+  return status;
+}
+
+
+int
+cmd_insert (struct lexer *lexer, struct dataset *ds UNUSED)
+{
+  enum syntax_mode syntax_mode = GETL_INTERACTIVE;
+  enum error_mode error_mode = ERRMODE_CONTINUE;
+  char *filename = NULL;
+  int status = parse_insert (lexer, &filename);
+  bool cd = false;
+
+  if ( CMD_SUCCESS != status)
+    return status;
+
+  lex_get (lexer);
+
+  while ( '.' != lex_token (lexer))
+    {
+      if (lex_match_id (lexer, "SYNTAX"))
+	{
+	  lex_match (lexer, '=');
+	  if ( lex_match_id (lexer, "INTERACTIVE") )
+	    syntax_mode = GETL_INTERACTIVE;
+	  else if ( lex_match_id (lexer, "BATCH"))
+	    syntax_mode = GETL_BATCH;
+	  else
+	    {
+	      lex_error(lexer,
+			_("Expecting BATCH or INTERACTIVE after SYNTAX."));
+	      return CMD_FAILURE;
+	    }
+	}
+      else if (lex_match_id (lexer, "CD"))
+	{
+	  lex_match (lexer, '=');
+	  if ( lex_match_id (lexer, "YES") )
+	    {
+	      cd = true;
+	    }
+	  else if ( lex_match_id (lexer, "NO"))
+	    {
+	      cd = false;
+	    }
+	  else
+	    {
+	      lex_error (lexer, _("Expecting YES or NO after CD."));
+	      return CMD_FAILURE;
+	    }
+	}
+      else if (lex_match_id (lexer, "ERROR"))
+	{
+	  lex_match (lexer, '=');
+	  if ( lex_match_id (lexer, "CONTINUE") )
+	    {
+	      error_mode = ERRMODE_CONTINUE;
+	    }
+	  else if ( lex_match_id (lexer, "STOP"))
+	    {
+	      error_mode = ERRMODE_STOP;
+	    }
+	  else
+	    {
+	      lex_error (lexer, _("Expecting CONTINUE or STOP after ERROR."));
+	      return CMD_FAILURE;
+	    }
+	}
+
+      else
+	{
+	  lex_error (lexer, _("Unexpected token: `%s'."),
+		     lex_token_representation (lexer));
+
+	  return CMD_FAILURE;
+	}
+    }
+
+  status = lex_end_of_command (lexer);
+
+  if ( status == CMD_SUCCESS)
+    {
+      struct source_stream *ss = lex_get_source_stream (lexer);
+
+      assert (filename);
+      getl_include_source (ss, create_syntax_file_source (filename),
+			   syntax_mode,
+			   error_mode);
+
+      if ( cd )
+	{
+	  char *directory = dir_name (filename);
+	  chdir (directory);
+	  free (directory);
+	}
+
+      free (filename);
+    }
+
+  return status;
+}
+
+
+static int
+parse_insert (struct lexer *lexer, char **filename)
+{
   char *target_fn;
+  char *relative_filename;
 
   /* Skip optional FILE=. */
   if (lex_match_id (lexer, "FILE"))
@@ -45,23 +175,24 @@ cmd_include (struct lexer *lexer, struct dataset *ds UNUSED)
   if (lex_token (lexer) != T_ID && lex_token (lexer) != T_STRING)
     {
       lex_error (lexer, _("expecting file name"));
-      return CMD_CASCADING_FAILURE;
+      return CMD_FAILURE;
     }
 
   target_fn = ds_cstr (lex_tokstr (lexer));
 
-  ss = lex_get_source_stream (lexer);
-  found_fn = fn_search_path (target_fn, getl_include_path ( ss ));
+  relative_filename =
+    fn_search_path (target_fn,
+		    getl_include_path (lex_get_source_stream (lexer)));
 
-  if (found_fn != NULL)
+  if ( ! relative_filename)
     {
-      getl_include_source (ss, create_syntax_file_source (found_fn));
-      free (found_fn);
-    }
-  else
-    msg (SE, _("Can't find `%s' in include file search path."),
+      msg (SE, _("Can't find `%s' in include file search path."),
 	 target_fn);
+      return CMD_FAILURE;
+    }
 
-  lex_get (lexer);
-  return lex_end_of_command (lexer);
+  *filename = canonicalize_file_name (relative_filename);
+  free (relative_filename);
+
+  return CMD_SUCCESS;
 }
diff --git a/src/libpspp/ChangeLog b/src/libpspp/ChangeLog
index 6f3ddf519d..9c1b654d9c 100644
--- a/src/libpspp/ChangeLog
+++ b/src/libpspp/ChangeLog
@@ -1,3 +1,8 @@
+2007-09-05 John Darrington <john@darrington.wattle.id.au>
+
+	* getl.c: Add extra members to struct getl_source, to maintain the
+	error mode and the syntax_mode. 
+
 2007-07-25  Ben Pfaff  <blp@gnu.org>
 
 	* getl.c (getl_append_source): Add source to *end* of list.
diff --git a/src/libpspp/getl.c b/src/libpspp/getl.c
index b461712267..4d6338ce02 100644
--- a/src/libpspp/getl.c
+++ b/src/libpspp/getl.c
@@ -32,6 +32,8 @@ struct getl_source
     struct ll  ll;   /* Element in the sources list */
 
     struct getl_interface *interface;
+    enum syntax_mode syntax_mode;
+    enum error_mode error_mode;
   };
 
 struct source_stream
@@ -54,6 +56,26 @@ current_source (const struct source_stream *ss)
   return ll_data (ll, struct getl_source, ll );
 }
 
+enum syntax_mode
+source_stream_current_syntax_mode (const struct source_stream *ss)
+{
+  struct getl_source *cs = current_source (ss);
+
+  return cs->syntax_mode;
+}
+
+
+
+enum error_mode
+source_stream_current_error_mode (const struct source_stream *ss)
+{
+  struct getl_source *cs = current_source (ss);
+
+  return cs->error_mode;
+}
+
+
+
 /* Initialize getl. */
 struct source_stream *
 create_source_stream (const char *initial_include_path)
@@ -88,18 +110,26 @@ getl_add_include_dir (struct source_stream *ss, const char *path)
 
 /* Appends source S to the list of source files. */
 void
-getl_append_source (struct source_stream *ss, struct getl_interface *i)
+getl_append_source (struct source_stream *ss,
+		    struct getl_interface *i,
+		    enum syntax_mode syntax_mode,
+		    enum error_mode err_mode)
 {
   struct getl_source *s = xzalloc (sizeof ( struct getl_source ));
 
   s->interface = i ;
+  s->syntax_mode = syntax_mode;
+  s->error_mode = err_mode;
 
   ll_push_tail (&ss->sources, &s->ll);
 }
 
 /* Nests source S within the current source file. */
 void
-getl_include_source (struct source_stream *ss, struct getl_interface *i)
+getl_include_source (struct source_stream *ss,
+		     struct getl_interface *i,
+		     enum syntax_mode syntax_mode,
+		     enum error_mode err_mode)
 {
   struct getl_source *current = current_source (ss);
   struct getl_source *s = xzalloc (sizeof ( struct getl_source ));
@@ -108,6 +138,8 @@ getl_include_source (struct source_stream *ss, struct getl_interface *i)
 
   s->included_from = current ;
   s->includes  = NULL;
+  s->syntax_mode  = syntax_mode;
+  s->error_mode = err_mode;
   current->includes = s;
 
   ll_push_head (&ss->sources, &s->ll);
@@ -205,10 +237,9 @@ destroy_source_stream (struct source_stream *ss)
 
 /* Reads a single line into LINE.
    Returns true when a line has been read, false at end of input.
-   On success, sets *SYNTAX to the style of the syntax read. */
+*/
 bool
-getl_read_line (struct source_stream *ss, struct string *line,
-		enum getl_syntax *syntax)
+getl_read_line (struct source_stream *ss, struct string *line)
 {
   assert (ss != NULL);
   while (!ll_is_empty (&ss->sources))
@@ -216,12 +247,12 @@ getl_read_line (struct source_stream *ss, struct string *line,
       struct getl_source *s = current_source (ss);
 
       ds_clear (line);
-      if (s->interface->read (s->interface, line, syntax))
+      if (s->interface->read (s->interface, line))
         {
           while (s)
 	    {
 	      if (s->interface->filter)
-		s->interface->filter (s->interface, line, *syntax);
+		s->interface->filter (s->interface, line);
 	      s = s->included_from;
 	    }
 
diff --git a/src/libpspp/getl.h b/src/libpspp/getl.h
index 26c0649cfe..c039937798 100644
--- a/src/libpspp/getl.h
+++ b/src/libpspp/getl.h
@@ -25,7 +25,7 @@ struct string;
 struct getl_source;
 
 /* Syntax rules that apply to a given source line. */
-enum getl_syntax
+enum syntax_mode
   {
     /* Each line that begins in column 1 starts a new command.  A
        `+' or `-' in column 1 is ignored to allow visual
@@ -38,6 +38,16 @@ enum getl_syntax
     GETL_INTERACTIVE
   };
 
+enum error_mode
+  {
+    /* When errors are encountered, report the error and continue to
+       the next command. */
+    ERRMODE_CONTINUE,
+
+    /* When errors are encountered, abort the current stream. */
+    ERRMODE_STOP
+  };
+
 /* An abstract base class for objects which act as line buffers for the
    PSPP.  Ie anything which might contain content for the lexer */
 struct getl_interface
@@ -51,7 +61,7 @@ struct getl_interface
        Returns true if succesful, false on failure or at end of
        input. */
     bool  (*read)  (struct getl_interface *,
-                    struct string *, enum getl_syntax *);
+                    struct string *);
 
     /* Close and destroy the interface */
     void  (*close) (struct getl_interface *);
@@ -59,7 +69,7 @@ struct getl_interface
     /* Filter for current and all included sources, which may
        modify the line.  Usually null.  */
     void  (*filter) (struct getl_interface *,
-                     struct string *line, enum getl_syntax);
+                     struct string *line);
 
     /* Returns the name of the source */
     const char * (*name) (const struct getl_interface *);
@@ -71,6 +81,15 @@ struct getl_interface
 struct source_stream;
 
 struct source_stream * create_source_stream (const char *);
+
+enum syntax_mode source_stream_current_syntax_mode
+   (const struct source_stream *);
+
+
+enum error_mode source_stream_current_error_mode
+   (const struct source_stream *);
+
+
 void destroy_source_stream (struct source_stream *);
 
 void getl_clear_include_path (struct source_stream *);
@@ -80,11 +99,13 @@ const char * getl_include_path (const struct source_stream *);
 void getl_abort_noninteractive (struct source_stream *);
 bool getl_is_interactive (const struct source_stream *);
 
-bool getl_read_line (struct source_stream *, struct string *,
-		     enum getl_syntax *);
+bool getl_read_line (struct source_stream *, struct string *);
+
+void getl_append_source (struct source_stream *, struct getl_interface *s,
+			 enum syntax_mode, enum error_mode) ;
 
-void getl_append_source (struct source_stream *, struct getl_interface *s) ;
-void getl_include_source (struct source_stream *, struct getl_interface *s) ;
+void getl_include_source (struct source_stream *, struct getl_interface *s,
+			  enum syntax_mode, enum error_mode) ;
 
 const char * getl_source_name (const struct source_stream *);
 int getl_source_location (const struct source_stream *);
diff --git a/src/ui/gui/helper.c b/src/ui/gui/helper.c
index 6259965aaf..1ca0df3e48 100644
--- a/src/ui/gui/helper.c
+++ b/src/ui/gui/helper.c
@@ -178,7 +178,7 @@ execute_syntax (struct getl_interface *sss)
 
   lexer = lex_create (the_source_stream);
 
-  getl_append_source (the_source_stream, sss);
+  getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
 
   for (;;)
     {
diff --git a/src/ui/gui/syntax-editor-source.c b/src/ui/gui/syntax-editor-source.c
index 1cee28886c..289bd37a0f 100644
--- a/src/ui/gui/syntax-editor-source.c
+++ b/src/ui/gui/syntax-editor-source.c
@@ -67,8 +67,7 @@ location (const struct getl_interface *i)
 
 static bool
 read_line_from_buffer (struct getl_interface *i,
-		       struct string *line,
-		       enum getl_syntax *syntax_rules)
+		       struct string *line)
 {
   gchar *text;
   GtkTextIter next_line;
diff --git a/src/ui/terminal/command-line.c b/src/ui/terminal/command-line.c
index e152f70099..1205d4d5a4 100644
--- a/src/ui/terminal/command-line.c
+++ b/src/ui/terminal/command-line.c
@@ -189,7 +189,11 @@ parse_command_line (int argc, char **argv, struct source_stream *ss)
       char *pspprc_fn = fn_search_path ("rc", config_path);
       if (pspprc_fn != NULL)
         {
-	  getl_append_source (ss, create_syntax_file_source (pspprc_fn));
+	  getl_append_source (ss,
+			      create_syntax_file_source (pspprc_fn),
+			      GETL_BATCH,
+			      ERRMODE_CONTINUE
+			      );
 
           free (pspprc_fn);
         }
@@ -200,13 +204,20 @@ parse_command_line (int argc, char **argv, struct source_stream *ss)
       outp_configure_macro (argv[i]);
     else
       {
-	getl_append_source (ss, create_syntax_file_source (argv[i]));
+	getl_append_source (ss,
+			    create_syntax_file_source (argv[i]),
+			    GETL_BATCH,
+			    ERRMODE_CONTINUE
+			    );
         syntax_files++;
       }
 
   if (!syntax_files || interactive_mode)
     {
-      getl_append_source (ss, create_readln_source () );
+      getl_append_source (ss, create_readln_source (),
+			  GETL_INTERACTIVE,
+			  ERRMODE_CONTINUE
+			  );
       if (!cleared_device_defaults)
         outp_configure_add ("interactive");
     }
diff --git a/src/ui/terminal/read-line.c b/src/ui/terminal/read-line.c
index 2529dee40b..626b063533 100644
--- a/src/ui/terminal/read-line.c
+++ b/src/ui/terminal/read-line.c
@@ -101,12 +101,11 @@ readln_uninitialize (void)
 
 static bool
 read_interactive (struct getl_interface *s,
-                  struct string *line, enum getl_syntax *syntax)
+                  struct string *line)
 {
   struct readln_source *is  =
     (struct readln_source *) s ;
 
-  *syntax = GETL_INTERACTIVE;
   return is->interactive_func (line, prompt_get_style ());
 }
 
diff --git a/tests/automake.mk b/tests/automake.mk
index a67be09c55..d2648e5336 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -20,6 +20,7 @@ dist_TESTS = \
 	tests/command/filter.sh \
 	tests/command/flip.sh \
 	tests/command/import-export.sh \
+	tests/command/insert.sh \
 	tests/command/lag.sh \
 	tests/command/list.sh \
 	tests/command/loop.sh \
diff --git a/tests/command/insert.sh b/tests/command/insert.sh
new file mode 100755
index 0000000000..ba4f543a79
--- /dev/null
+++ b/tests/command/insert.sh
@@ -0,0 +1,253 @@
+#!/bin/sh
+
+# This program tests the INSERT command
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir  are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+	echo "NOT cleaning $TEMPDIR"
+     	return ; 
+     fi
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create wrapper 1"
+cat <<EOF > $TESTFILE
+INSERT 
+  FILE='$TEMPDIR/foo.sps'
+  SYNTAX=INTERACTIVE
+  .
+
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+#The following syntax intentionally omits periods from some lines
+#It's an example of "batch" syntax
+activity="create insert"
+cat <<EOF > $TEMPDIR/foo.sps
+input program.
++  loop #i = 1 to 100.
++    compute z = #i
++    end case.
++  end loop
+end file.
+end input program.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+#This command should fail
+activity="run program 1"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -eq 0 ] ; then fail ; fi
+
+
+activity="create wrapper 2"
+cat <<EOF > $TESTFILE
+INSERT 
+  FILE='$TEMPDIR/foo.sps'
+  SYNTAX=BATCH
+  .
+
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 2"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+# Now test the CD subcommand
+
+activity="mkdir 1"
+mkdir $TEMPDIR/Dir1
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create wrapper 3"
+cat <<EOF > $TESTFILE
+INSERT 
+  FILE='$TEMPDIR/Dir1/foo.sps'
+  CD=NO
+  .
+
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create wrapper 4"
+cat <<EOF > $TEMPDIR/Dir1/foo.sps
+INSERT 
+  FILE='bar.sps'
+  CD=NO
+  .
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create wrapper 5"
+cat <<EOF > $TEMPDIR/Dir1/bar.sps
+DATA LIST LIST /x *.
+BEGIN DATA.
+1
+2
+3
+END DATA.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+# This command should fail
+activity="run program 3"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -eq 0 ] ; then fail ; fi
+
+activity="create wrapper 6"
+cat <<EOF > $TESTFILE
+INSERT 
+  FILE='$TEMPDIR/Dir1/foo.sps'
+  CD=YES
+  .
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 4"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+# Now test the ERROR= feature
+
+activity="create wrapper 7"
+cat <<EOF > $TESTFILE
+INSERT 
+  FILE='$TEMPDIR/foo.sps'
+  ERROR=STOP.
+  .
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="create included file"
+cat <<EOF > $TEMPDIR/foo.sps
+DATA LIST NOTABLE LIST /x *.
+BEGIN DATA.
+1
+2
+3
+END DATA.
+
+* The following line is erroneous
+
+DISPLAY AKSDJ.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 5"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -ne 1 ] ; then no_result ; fi
+
+activity="examine output 1"
+diff $TEMPDIR/pspp.list - <<EOF
+$TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+warning: Error encountered while ERROR=STOP is effective.
+$TEMPDIR/foo.sps:10: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="create wrapper 8"
+cat <<EOF > $TESTFILE
+INSERT 
+  FILE='$TEMPDIR/foo.sps'
+  ERROR=CONTINUE.
+  .
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 6"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -ne 1 ] ; then no_result ; fi
+
+activity="examine output 2"
+diff $TEMPDIR/pspp.list - <<EOF
+$TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+
+       x
+--------
+    1.00 
+    2.00 
+    3.00 
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+
+pass;
-- 
2.30.2