Create two new exceptions:  IndentationError and TabError.  These are
used for indentation related errors.  This patch includes Ping's
improvements for indentation-related error messages.

Closes SourceForge patches #100734 and #100856.
diff --git a/Include/errcode.h b/Include/errcode.h
index b872e18..0683317 100644
--- a/Include/errcode.h
+++ b/Include/errcode.h
@@ -30,8 +30,10 @@
 #define E_NOMEM		15	/* Ran out of memory */
 #define E_DONE		16	/* Parsing complete */
 #define E_ERROR		17	/* Execution error */
-#define E_INDENT	18	/* Invalid indentation detected */
-#define E_OVERFLOW      19      /* Node had too many children */
+#define E_TABSPACE	18	/* Invalid indentation detected */
+#define E_OVERFLOW      19	/* Node had too many children */
+#define E_TOODEEP	20	/* Too many indentation levels */
+#define E_DEDENT	21	/* No matching outer block for dedent */
 
 #ifdef __cplusplus
 }
diff --git a/Include/parsetok.h b/Include/parsetok.h
index 5244e3f..71e7d89 100644
--- a/Include/parsetok.h
+++ b/Include/parsetok.h
@@ -22,6 +22,8 @@
     int lineno;
     int offset;
     char *text;
+    int token;
+    int expected;
 } perrdetail;
 
 extern DL_IMPORT(node *) PyParser_ParseString(char *, grammar *, int,
diff --git a/Lib/test/output/test_exceptions b/Lib/test/output/test_exceptions
index 5605114..9e021d2 100644
--- a/Lib/test/output/test_exceptions
+++ b/Lib/test/output/test_exceptions
@@ -28,6 +28,10 @@
 spam
 SyntaxError
 spam
+IndentationError
+spam
+TabError
+spam
 SystemError
 (hard to reproduce)
 spam
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 7bc515c..06861df 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -86,6 +86,14 @@
 try: exec '/\n'
 except SyntaxError: pass
 
+r(IndentationError)
+
+r(TabError)
+# can only be tested under -tt, and is the only test for -tt
+#try: compile("try:\n\t1/0\n    \t1/0\nfinally:\n pass\n", '<string>', 'exec')
+#except TabError: pass
+#else: raise TestFailed
+
 r(SystemError)
 print '(hard to reproduce)'
 
diff --git a/Parser/parser.c b/Parser/parser.c
index ee40f21..b74de7f 100644
--- a/Parser/parser.c
+++ b/Parser/parser.c
@@ -205,11 +205,12 @@
 }
 
 int
-PyParser_AddToken(ps, type, str, lineno)
+PyParser_AddToken(ps, type, str, lineno, expected_ret)
 	register parser_state *ps;
 	register int type;
 	char *str;
 	int lineno;
+	int *expected_ret;
 {
 	register int ilabel;
 	int err;
@@ -285,6 +286,15 @@
 		
 		/* Stuck, report syntax error */
 		D(printf(" Error.\n"));
+		if (expected_ret) {
+			if (s->s_lower == s->s_upper - 1) {
+				/* Only one possible expected token */
+				*expected_ret = ps->p_grammar->
+				    g_ll.ll_label[s->s_lower].lb_type;
+			}
+			else 
+		        	*expected_ret = -1;
+		}
 		return E_SYNTAX;
 	}
 }
diff --git a/Parser/parser.h b/Parser/parser.h
index 6087373..d0df8cf 100644
--- a/Parser/parser.h
+++ b/Parser/parser.h
@@ -38,7 +38,8 @@
 
 parser_state *PyParser_New(grammar *g, int start);
 void PyParser_Delete(parser_state *ps);
-int PyParser_AddToken(parser_state *ps, int type, char *str, int lineno);
+int PyParser_AddToken(parser_state *ps, int type, char *str, int lineno,
+                      int *expected_ret);
 void PyGrammar_AddAccelerators(grammar *g);
 
 #ifdef __cplusplus
diff --git a/Parser/parsetok.c b/Parser/parsetok.c
index 9ac1606..9d090f1 100644
--- a/Parser/parsetok.c
+++ b/Parser/parsetok.c
@@ -139,8 +139,8 @@
 			strncpy(str, a, len);
 		str[len] = '\0';
 		if ((err_ret->error =
-		     PyParser_AddToken(ps, (int)type, str,
-				       tok->lineno)) != E_OK) {
+		     PyParser_AddToken(ps, (int)type, str, tok->lineno,
+				       &(err_ret->expected))) != E_OK) {
 			if (err_ret->error != E_DONE)
 				PyMem_DEL(str);
 			break;
diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c
index 0ef3fc0..d4ec345 100644
--- a/Parser/tokenizer.c
+++ b/Parser/tokenizer.c
@@ -412,13 +412,13 @@
 	struct tok_state *tok;
 {
 	if (tok->alterror) {
-		tok->done = E_INDENT;
+		tok->done = E_TABSPACE;
 		tok->cur = tok->inp;
 		return 1;
 	}
 	if (tok->altwarning) {
-		PySys_WriteStderr("%s: inconsistent tab/space usage\n",
-			tok->filename);
+		PySys_WriteStderr("%s: inconsistent use of tabs and spaces "
+                                  "in indentation\n", tok->filename);
 		tok->altwarning = 0;
 	}
 	return 0;
@@ -484,9 +484,7 @@
 			else if (col > tok->indstack[tok->indent]) {
 				/* Indent -- always one */
 				if (tok->indent+1 >= MAXINDENT) {
-					PySys_WriteStderr(
-						"excessive indent\n");
-					tok->done = E_TOKEN;
+					tok->done = E_TOODEEP;
 					tok->cur = tok->inp;
 					return ERRORTOKEN;
 				}
@@ -506,9 +504,7 @@
 					tok->indent--;
 				}
 				if (col != tok->indstack[tok->indent]) {
-					PySys_WriteStderr(
-						"inconsistent dedent\n");
-					tok->done = E_TOKEN;
+					tok->done = E_DEDENT;
 					tok->cur = tok->inp;
 					return ERRORTOKEN;
 				}
diff --git a/Python/exceptions.c b/Python/exceptions.c
index f766ba5..a70e6c6 100644
--- a/Python/exceptions.c
+++ b/Python/exceptions.c
@@ -68,6 +68,11 @@
       |\n\
       +-- AttributeError\n\
       +-- SyntaxError\n\
+      |    |\n\
+      |    +-- IndentationError\n\
+      |         |\n\
+      |         +-- TabError\n\
+      |\n\
       +-- TypeError\n\
       +-- AssertionError\n\
       +-- LookupError\n\
@@ -783,6 +788,12 @@
 static char
 MemoryError__doc__[] = "Out of memory.";
 
+static char
+IndentationError__doc__[] = "Improper indentation.";
+
+static char
+TabError__doc__[] = "Improper mixture of spaces and tabs.";
+
 
 
 /* module global functions */
@@ -817,6 +828,8 @@
 PyObject *PyExc_RuntimeError;
 PyObject *PyExc_NotImplementedError;
 PyObject *PyExc_SyntaxError;
+PyObject *PyExc_IndentationError;
+PyObject *PyExc_TabError;
 PyObject *PyExc_SystemError;
 PyObject *PyExc_SystemExit;
 PyObject *PyExc_UnboundLocalError;
@@ -878,6 +891,10 @@
  {"AttributeError",     &PyExc_AttributeError, 0, AttributeError__doc__},
  {"SyntaxError",        &PyExc_SyntaxError,    0, SyntaxError__doc__,
   SyntaxError_methods, SyntaxError__classinit__},
+ {"IndentationError",   &PyExc_IndentationError, &PyExc_SyntaxError,
+  IndentationError__doc__},
+ {"TabError",   &PyExc_TabError, &PyExc_IndentationError,
+  TabError__doc__},
  {"AssertionError",     &PyExc_AssertionError, 0, AssertionError__doc__},
  {"LookupError",        &PyExc_LookupError,    0, LookupError__doc__},
  {"IndexError",         &PyExc_IndexError,     &PyExc_LookupError,
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 74dffae..9cc8072 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -14,6 +14,7 @@
 
 #include "grammar.h"
 #include "node.h"
+#include "token.h"
 #include "parsetok.h"
 #include "errcode.h"
 #include "compile.h"
@@ -983,8 +984,9 @@
 err_input(err)
 	perrdetail *err;
 {
-	PyObject *v, *w;
+	PyObject *v, *w, *errtype;
 	char *msg = NULL;
+	errtype = PyExc_SyntaxError;
 	v = Py_BuildValue("(ziiz)", err->filename,
 			    err->lineno, err->offset, err->text);
 	if (err->text != NULL) {
@@ -993,7 +995,17 @@
 	}
 	switch (err->error) {
 	case E_SYNTAX:
-		msg = "invalid syntax";
+		errtype = PyExc_IndentationError;
+		if (err->expected == INDENT)
+			msg = "expected an indented block";
+		else if (err->token == INDENT)
+			msg = "unexpected indent";
+		else if (err->token == DEDENT)
+			msg = "unexpected unindent";
+		else {
+			errtype = PyExc_SyntaxError;
+			msg = "invalid syntax";
+		}
 		break;
 	case E_TOKEN:
 		msg = "invalid token";
@@ -1009,12 +1021,21 @@
 	case E_EOF:
 		msg = "unexpected EOF while parsing";
 		break;
-	case E_INDENT:
+	case E_TABSPACE:
+		errtype = PyExc_TabError;
 		msg = "inconsistent use of tabs and spaces in indentation";
 		break;
 	case E_OVERFLOW:
 		msg = "expression too long";
 		break;
+	case E_DEDENT:
+		errtype = PyExc_IndentationError;
+		msg = "unindent does not match any outer indentation level";
+		break;
+	case E_TOODEEP:
+		errtype = PyExc_IndentationError;
+		msg = "too many levels of indentation";
+		break;
 	default:
 		fprintf(stderr, "error=%d\n", err->error);
 		msg = "unknown parsing error";
@@ -1022,7 +1043,7 @@
 	}
 	w = Py_BuildValue("(sO)", msg, v);
 	Py_XDECREF(v);
-	PyErr_SetObject(PyExc_SyntaxError, w);
+	PyErr_SetObject(errtype, w);
 	Py_XDECREF(w);
 }