diff --git a/doc/groff.texi b/doc/groff.texi
index 83d65c03..c7e7afdf 100644
--- a/doc/groff.texi
+++ b/doc/groff.texi
@@ -13514,8 +13514,8 @@ next four text lines because all but the last are interrupted with
 
 Input traps are associated with the environment (@pxref{Environments});
 switching to another environment disables the current input trap, and
-going back reactivates it, restoring the count of already processed
-lines.
+going back reactivates it, restoring the count of lines already
+processed.
 
 @c ---------------------------------------------------------------------
 
@@ -13840,7 +13840,7 @@ baselines and thus does not increase the value interpolated by
 @cindex @code{boxa} request, and @code{dn} (@code{dl})
 After completing a diversion, the writable registers @code{dn} and
 @code{dl} contain the vertical and horizontal size of the diversion.
-Only the just-processed lines are counted: for the computation of
+Only the lines just processed are counted: for the computation of
 @code{dn} and @code{dl}, the requests @code{da} and @code{boxa} are
 handled as if @code{di} and @code{box} had been used,
 respectively---lines that have been already stored in the diversion
@@ -14018,13 +14018,15 @@ requests.
 @cindex environments
 
 It happens frequently that some text should be printed in a certain
-format regardless of what may be in effect at the time, for example, in
-a trap invoked macro to print headers and footers.  To solve this
-@code{gtroff} processes text in @dfn{environments}.  An environment
-contains most of the parameters that control text processing.  It is
-possible to switch amongst these environments; by default @code{gtroff}
-processes text in environment@tie{}0.  The following is the information
-kept in an environment.
+format regardless of what may be in effect at the time.  For example,
+headers and footers are typically rendered by macros called by traps,
+and their text should be set in a consistent style and size regardless
+of how the body text is being rendered at that time.  To address this
+need, @code{roff} systems process text in an @dfn{environment} which
+stores most of the parameters that control text processing.  It is
+possible to create new environments and switch among them; the default
+is named @samp{0} (zero).  The following is the information kept in an
+environment.
 
 @itemize @bullet
 @item
@@ -14037,7 +14039,7 @@ spacing, indentation, line numbering, centering, right-justifying,
 underlining, hyphenation data)
 
 @item
-fill and adjust mode
+filling enablement, adjustment enablement and mode
 
 @item
 tab stops, tab and leader characters, escape character, no-break and
@@ -14053,25 +14055,19 @@ input traps
 drawing and fill colours
 @end itemize
 
-These environments may be given arbitrary names (@pxref{Identifiers}.)
-Old versions of @code{troff} only had environments named @samp{0},
-@samp{1}, and @samp{2}.
-
-@DefreqList {ev, [@Var{env}]}
+@DefreqList {ev, [@Var{ident}]}
 @DefregListEndx {.ev}
 @cindex switching environments (@code{ev})
 @cindex environment, switching (@code{ev})
 @cindex environment number/name register (@code{.ev})
-Switch to another environment.  The argument @var{env} is the name of
-the environment to switch to.  With no argument, @code{gtroff} switches
-back to the previous environment.  There is no limit on the number of
-named environments; they are created the first time that they are
-referenced.  The read-only string-valued register @code{.ev} contains
-the name or number of the current environment.
+Enter the environment @var{ident}, which is created if it does not
+already exist.  With no argument, GNU @code{troff} switches back to the
+previous environment.  The read-only string-valued register @code{.ev}
+contains the name of the current environment.
 
 A call to @code{ev} (with argument) pushes the previously active
 environment onto a stack.  If, say, environments @samp{foo},
-@samp{bar}, and @samp{zap} are called (in that order), the first
+@samp{bar}, and @samp{zap} are entered (in that order), the first
 @code{ev} request without parameter switches back to environment
 @samp{bar} (which is popped off the stack), and a second call switches
 back to environment @samp{foo}.
@@ -14086,10 +14082,10 @@ Here is an example:
 .ll -.5i
 .ev
 
-...
+@r{@dots{}}
 
 .ev footnote-env
-\(dg Note the large, friendly letters.
+\[dg] Note the large, friendly letters.
 .ev
 @endExample
 @endDefreq
@@ -15699,6 +15695,11 @@ called with @option{-T}.
 @code{lf} request differently.  For them, its @var{line} argument
 changes the line number of the @emph{current} line.
 
+@cindex environment availability and naming, incompatibilities with @acronym{AT&T} @code{troff}
+@acronym{AT&T} @code{troff} had only environments named @samp{0},
+@samp{1}, and @samp{2}.  In GNU @code{troff}, an environment may be
+given any valid identifier for a name (@pxref{Identifiers}.)
+
 @cindex input level in delimited arguments
 @cindex delimited arguments, incompatibilities with @acronym{AT&T} @code{troff}
 Normally, GNU @code{troff} preserves the input level in delimited
diff --git a/src/roff/troff/env.cpp b/src/roff/troff/env.cpp
index 459aee79..00efeb5b 100644
--- a/src/roff/troff/env.cpp
+++ b/src/roff/troff/env.cpp
@@ -61,8 +61,6 @@ struct env_list {
 };
 
 env_list *env_stack;
-const int NENVIRONMENTS = 10;
-environment *env_table[NENVIRONMENTS];
 dictionary env_dictionary(10);
 environment *curenv;
 static int next_line_number = 0;
@@ -267,9 +265,12 @@ int font_size::to_units()
 // we can't do this in a static constructor because various dictionaries
 // have to get initialized first
 
+static symbol default_environment_name("0");
+
 void init_environments()
 {
-  curenv = env_table[0] = new environment("0");
+  curenv = new environment(default_environment_name);
+  (void)env_dictionary.lookup(default_environment_name, curenv);
 }
 
 void tab_character()
@@ -1100,49 +1101,14 @@ node *environment::extract_output_line()
 
 void environment_switch()
 {
-  int pop = 0;	// 1 means pop, 2 means pop but no error message on underflow
-  if (curenv->is_dummy())
-    error("can't switch environments when current environment is dummy");
-  else if (!has_arg())
-    pop = 1;
-  else {
-    symbol nm;
-    if (!tok.delimiter()) {
-      // It looks like a number.
-      int n;
-      if (get_integer(&n)) {
-	if (n >= 0 && n < NENVIRONMENTS) {
-	  env_stack = new env_list(curenv, env_stack);
-	  if (env_table[n] == 0)
-	    env_table[n] = new environment(i_to_a(n));
-	  curenv = env_table[n];
-	}
-	else
-	  nm = i_to_a(n);
-      }
-      else
-	pop = 2;
-    }
-    else {
-      nm = get_long_name(true /* required */);
-      if (nm.is_null())
-	pop = 2;
-    }
-    if (!nm.is_null()) {
-      environment *e = (environment *)env_dictionary.lookup(nm);
-      if (!e) {
-	e = new environment(nm);
-	(void)env_dictionary.lookup(nm, e);
-      }
-      env_stack = new env_list(curenv, env_stack);
-      curenv = e;
-    }
+  if (curenv->is_dummy()) {
+    error("cannot switch out of dummy environment");
+    return;
   }
-  if (pop) {
-    if (env_stack == 0) {
-      if (pop == 1)
-	error("environment stack underflow");
-    }
+  symbol nm = get_long_name();
+  if (nm.is_null()) {
+    if (env_stack == 0)
+      error("environment stack underflow");
     else {
       int seen_space = curenv->seen_space;
       int seen_eol   = curenv->seen_eol;
@@ -1156,34 +1122,34 @@ void environment_switch()
       delete tem;
     }
   }
+  else {
+    environment *e = (environment *)env_dictionary.lookup(nm);
+    if (!e) {
+      e = new environment(nm);
+      (void)env_dictionary.lookup(nm, e);
+    }
+    env_stack = new env_list(curenv, env_stack);
+    curenv = e;
+  }
   skip_line();
 }
 
 void environment_copy()
 {
-  symbol nm;
   environment *e=0;
   tok.skip();
-  if (!tok.delimiter()) {
-    // It looks like a number.
-    int n;
-    if (get_integer(&n)) {
-      if (n >= 0 && n < NENVIRONMENTS)
-	e = env_table[n];
-      else
-	nm = i_to_a(n);
-    }
-  }
-  else
-    nm = get_long_name(true /* required */);
-  if (!e && !nm.is_null())
-    e = (environment *)env_dictionary.lookup(nm);
-  if (e == 0) {
-    error("No environment to copy from");
+  symbol nm = get_long_name();
+  if (nm.is_null()) {
+    error("no environment to copy from");
     return;
   }
   else
+    e = (environment *)env_dictionary.lookup(nm);
+  if (e)
     curenv->copy(e);
+  else
+    error("cannot copy from nonexistent environment '%1'",
+	  nm.contents());
   skip_line();
 }
 
@@ -3378,15 +3344,6 @@ void print_env()
 {
   errprint("Current Environment:\n");
   curenv->print_env();
-  for (int i = 0; i < NENVIRONMENTS; i++) {
-    if (env_table[i]) {
-      errprint("Environment %1:\n", i);
-      if (env_table[i] != curenv)
-	env_table[i]->print_env();
-      else
-	errprint("  current\n");
-    }
-  }
   dictionary_iterator iter(env_dictionary);
   symbol s;
   environment *e;
