diff --git a/doc/teapot.lyx b/doc/teapot.lyx
index d26cd0f..8c8c726 100644
--- a/doc/teapot.lyx
+++ b/doc/teapot.lyx
@@ -4625,7 +4625,7 @@ save-tbl
 file
 \series default
 \emph default
-  
+ 
 \end_layout
 
 \begin_layout Description
@@ -4642,7 +4642,7 @@ save-csv
 file
 \series default
 \emph default
-  
+ 
 \end_layout
 
 \begin_layout Description
@@ -4659,7 +4659,7 @@ save-latex
 file
 \series default
 \emph default
-  
+ 
 \end_layout
 
 \begin_layout Description
@@ -4676,7 +4676,7 @@ save-context
 file
 \series default
 \emph default
-  
+ 
 \end_layout
 
 \begin_layout Description
@@ -4855,6 +4855,11 @@ Location Cell labels and the
 &()
 \family default
  function have this type, but there are no location constant literals.
+ However, 
+\family typewriter
+&(3,2,1),
+\family default
+ for example, acts very much like a location constant literal.
 \end_layout
 
 \begin_layout Description
@@ -5041,7 +5046,7 @@ y
 x
 \family typewriter
 \emph default
-=
+~=
 \family default
 \emph on
 y
@@ -5489,7 +5494,8 @@ y
 z
 \emph default
  is omitted, the coordinate of the cell is used.
- The second form is in fact a no-op, but it is allowed for convenience
+ The second form is in fact a no-op, but it is allowed for convenience and
+ consistency of expressions.
 \end_layout
 
 \begin_layout Description
@@ -5515,8 +5521,21 @@ relative.
 \begin_inset Quotes erd
 \end_inset
 
- Thus R(-1) returns the value of the cell immediately to the left of this
- one.
+ Thus 
+\family sans
+\series bold
+R
+\series default
+(-1)
+\family default
+ returns the value of the cell immediately to the left of this one, and
+\family sans
+\series bold
+ R
+\series default
+(,,1)
+\family default
+ returns the same cell as this one but on the following layer.
 \end_layout
 
 \begin_layout Description
@@ -5541,8 +5560,181 @@ displaced (by).
 \begin_inset Quotes erd
 \end_inset
 
- Thus, D(-1) returns the location of the cell immediately to the left of
- this one.
+ Thus, 
+\family sans
+\series bold
+D
+\series default
+(-1)
+\family default
+ returns the location of the cell immediately to the left of this one, and
+\family sans
+\series bold
+ D
+\series default
+(,2,1)
+\family default
+ returns the location of the cell two below this one on the following layer.
+\end_layout
+
+\begin_layout Description
+X(
+\series medium
+label
+\emph on
+
+\begin_inset space ~
+\end_inset
+
+to,
+\begin_inset space ~
+\end_inset
+
+
+\emph default
+label
+\emph on
+
+\begin_inset space ~
+\end_inset
+
+from,
+\begin_inset space ~
+\end_inset
+
+
+\emph default
+[bool
+\emph on
+
+\begin_inset space ~
+\end_inset
+
+fix_x
+\emph default
+]
+\emph on
+,
+\begin_inset space ~
+\end_inset
+
+
+\emph default
+[bool
+\emph on
+
+\begin_inset space ~
+\end_inset
+
+fix_y
+\emph default
+]
+\emph on
+,
+\begin_inset space ~
+\end_inset
+
+
+\emph default
+[bool
+\emph on
+
+\begin_inset space ~
+\end_inset
+
+fix_z
+\emph default
+]
+\series default
+) 
+\begin_inset Quotes eld
+\end_inset
+
+Excel reference
+\begin_inset Quotes erd
+\end_inset
+
+: returns the value of the cell at a computed target location.
+ This target location is the one reached from the current cell via the same
+ offset as the cell with label 
+\emph on
+to
+\emph default
+ has from label 
+\emph on
+from.
+
+\emph default
+ The idea is that if you label the source of data you want to reference,
+ say with 
+\family sans
+SRC
+\family default
+, and the location of some place you want to start referring to it with
+ 
+\family sans
+REF
+\family default
+, then you can use 
+\family sans
+\series bold
+X
+\series default
+(SRC,REF)
+\family default
+ to refer to the source data, and fill this formula to neighboring cells
+ to refer to the neighbors of the source, and it will all continue to work
+ if either the source or the reference is moved around in the sheet.
+ 
+\begin_inset Newline newline
+\end_inset
+
+If the 
+\emph on
+fix_DIM
+\emph default
+ argument is present and positive, then the corresponding coordinate of
+ the target cell is set to match that of 
+\emph on
+to
+\emph default
+.
+ This corresponds to fixing the row or column (or layer) of the reference,
+ as with a 
+\begin_inset Quotes eld
+\end_inset
+
+$
+\begin_inset Quotes erd
+\end_inset
+
+ character in Excel.
+ Thus 
+\family sans
+\series bold
+X
+\series default
+(SRC,REF,1,1,1)
+\family default
+ is identical to 
+\family sans
+@(SRC)
+\family default
+, but you should certainly prefer the latter for clarity of expression.
+\begin_inset Newline newline
+\end_inset
+
+There is a corresponding location function 
+\family sans
+X&()
+\family default
+ as well, which takes exactly the same arguments with the same meanings,
+ but it is rarely needed.
+ It is provided for completeness.
+\begin_inset Newline newline
+\end_inset
+
+See the FAQ below for further discussion of cell references.
 \end_layout
 
 \begin_layout Description
@@ -5904,7 +6096,7 @@ clock
 
 condition
 \emph default
-,[location[,location]) 
+,[location[,location])
 \series default
  conditionally clocks the specified cell if the condition is not 0.
  If two locations are given, all cells in that range will be clocked.
@@ -7264,8 +7456,22 @@ z
  position of the given location, of the currently updated cell if none is
  given.
  These functions are usually used in combination with the @ function for
- relative relations to other cells.
+ relative relations to other cell, but see also the convenience functions
  
+\family sans
+\series bold
+R
+\series default
+()
+\family default
+ and 
+\family sans
+\series bold
+D
+\series default
+()
+\family default
+ for relative references.
 \end_layout
 
 \begin_layout Subsection
@@ -7763,11 +7969,13 @@ X
 \end_layout
 
 \begin_layout Quote
-eval(&((@(X)>=0)+x(BAD),y(BAD),z(BAD)))
+eval(BAD + &((@(X)>=0),0,0))
 \end_layout
 
 \begin_layout Standard
-The cell labelled 
+Note this is making use of the fact that you can add locations in the natural
+ way.
+ The cell labelled 
 \family typewriter
 BAD
 \family default
@@ -7786,5 +7994,190 @@ BAD
  
 \end_layout
 
+\begin_layout Subsection
+But my references don't do the right thing when I move or copy them!
+\end_layout
+
+\begin_layout Standard
+If you are used to other spreadsheets, you have probably noticed that references
+ like 
+\family sans
+@(0,1,0)
+\family default
+ (for the start of the second row) or 
+\family sans
+@(MYDATA) 
+\family default
+(for a location) are 
+\begin_inset Quotes eld
+\end_inset
+
+absolute
+\begin_inset Quotes erd
+\end_inset
+
+ – they always refer to the same location no matter where in the spreadsheet
+ they occur.
+ And of course sometimes it is convenient, for example when making a column
+ of consecutive numbers, to refer to nearby cells in a relative manner,
+ either with say 
+\family sans
+@(x(),y()-1,z())+1
+\family default
+ or 
+\family sans
+@(&()+&(0,-1,0))+1
+\family default
+ or just 
+\family sans
+R(,-1)+1
+\family default
+ to add one to the value of the cell just above.
+ Then you can fill that expression down and get your column of consecutive
+ numbers.
+\end_layout
+
+\begin_layout Standard
+But these sorts of relative expressions only keep working if the cells move
+ together with the cells they refer to.
+ If for example you have a row of cells that are all referring to the row
+ above with a relative reference (like 
+\family sans
+R(,-1)
+\family default
+) and you insert another row in between them, your references will be all
+ messed up.
+ There is value to 
+\begin_inset Quotes eld
+\end_inset
+
+Excel-style
+\begin_inset Quotes erd
+\end_inset
+
+ references that can be used to fill and which also can move around while
+ still just 
+\begin_inset Quotes eld
+\end_inset
+
+referring to what you want.
+\begin_inset Quotes erd
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+To provide for this need, teapot has a function 
+\family sans
+X(SRC, REF)
+\family default
+ to retrieve the value of the cell labeled 
+\family sans
+SRC
+\family default
+ 
+\bar under
+from
+\bar default
+ the cell labeled 
+\family sans
+REF
+\family default
+.
+ If the so-labeled cells move around (either the source or the reference)
+ it will still work.
+ This is not particularly useful in and of itself; what makes it useful
+ is that from a cell other than 
+\family sans
+REF
+\family default
+, it gives you the value of the cell that stands in the same relation to
+ 
+\family sans
+SRC
+\family default
+ as that cell stands to 
+\family sans
+REF
+\family default
+.
+ So in the cell to the right of 
+\family sans
+REF
+\family default
+, it will give you the value of the cell to the right of 
+\family sans
+SRC
+\family default
+; in the cell below, it gives you the cell below 
+\family sans
+SRC
+\family default
+, etc.
+ Now you can fill a block of cells around 
+\family sans
+REF
+\family default
+ with formulas contaning 
+\family sans
+X(SRC,REF)
+\family default
+ and they will refer to the analogous block of cells around 
+\family sans
+SRC
+\family default
+.
+ 
+\end_layout
+
+\begin_layout Standard
+Sometimes you want to make this kind of reference but fix one of the coordinates
+ but not the others; 
+\family sans
+X()
+\family default
+ has optional flags for this, as well, so that 
+\family sans
+X(SRC,REF,,,1)
+\family default
+ will always be on the same layer as 
+\family sans
+SRC
+\family default
+ regardless of what layer it is called from or what layer 
+\family sans
+REF
+\family default
+ is on.
+ Thus 
+\family sans
+X(SRC,REF,1,1,1)
+\family default
+ is just 
+\family sans
+@(SRC)
+\family default
+, but the intent of the latter is much clearer.
+\end_layout
+
+\begin_layout Standard
+You might ask as a follow-up question: Isnt 
+\family sans
+X(SRC, REF)
+\family default
+ much more cumbersome than just referring to cells by coordinate and then
+ letting Excel just do the right thing as you copy and move either that
+ formula or the referred-to data? The response to this is that in a typical
+ spreadsheet, there are only a small number of fundamental references, and
+ all other references derive from them in this way.
+ So you generally only need a few labels, and by taking just a little extra
+ time to apply those labels and refer to them in initial formulas, you are
+ making the semantics of your references much clearer and in essence documenting
+ them within your spreadsheet.
+ This extra effort will therefore be repaid in an easier-to-use, easier-to-under
+stand, and easier-to-maintain and update spreadsheet.
+\end_layout
+
 \end_body
 \end_document
diff --git a/src/common/eval.c b/src/common/eval.c
index 6799330..86f48be 100644
--- a/src/common/eval.c
+++ b/src/common/eval.c
@@ -18,6 +18,7 @@
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
+extern char *strdup(const char* s);
 #include <string.h>
 
 
@@ -44,26 +45,40 @@ Token tcopy(Token n)
   return result;
 }
 /*}}}*/
+
 /* tfree     -- free dynamic data of token */ /*{{{*/
 void tfree(Token *n)
 {
-  if (n->type==STRING)
+  Token fake;
+  fake.type = INT;
+  tfree_protected(n, fake);
+}
+/*}}}*/
+
+/* tfree_protected     -- free dynamic data of token but not if same as protected */ /*{{{*/
+void tfree_protected(Token *n, const Token dontfree)
+{
+  if (n->type == STRING &&
+      (dontfree.type != STRING || n->u.string != dontfree.u.string))
   {
     free(n->u.string);
     n->u.string=(char*)0;
   }
-  else if (n->type==EEK)
+  else if (n->type == EEK &&
+	   (dontfree.type != EEK || n->u.err != dontfree.u.err))
   {
     free(n->u.err);
     n->u.err=(char*)0;
   }
-  else if (n->type==LIDENT)
+  else if (n->type==LIDENT &&
+	   (dontfree.type != LIDENT || n->u.lident != dontfree.u.lident))
   {
     free(n->u.lident);
     n->u.lident=(char*)0;
   }
 }
 /*}}}*/
+
 /* tvecfreetoks  -- free the tokens in vector of pointer to tokens */ /*{{{*/
 void tvecfreetoks(Token **tvec)
 {
@@ -177,7 +192,7 @@ Token tadd(Token l, Token r)
   {
     result.type=EEK;
     len = strlen(_("wrong types for + operator"));
-    buf = malloc(len + 128);
+    buf = malloc(len + 5 + 2*MAX_TYPE_NAME_LENGTH + 1);
     strcpy(buf, _("wrong types for + operator"));
     snprintf(buf + len, 128, ": %s + %s", Type_Name[l.type], Type_Name[r.type]);
     result.u.err = buf;
@@ -706,6 +721,17 @@ Token tfuncall(Token *ident, int argc, Token argv[])
   return tfunc[ident->u.fident].func(argc, argv);
 }
 /*}}}*/
+
+static Token relational_type_mismatch(Type l, Type r)
+{
+  Token mismatch;
+  mismatch.type = EEK;
+  char *templ = _("Type mismatch: cannot compare %s and %s");
+  mismatch.u.err = malloc(strlen(templ) + 2*MAX_TYPE_NAME_LENGTH + 1);
+  sprintf(mismatch.u.err, templ, Type_Name[l], Type_Name[r]);
+  return mismatch;
+}
+
 /* tlt       -- < operator */ /*{{{*/
 Token tlt(Token l, Token r)
 {
@@ -787,12 +813,7 @@ Token tlt(Token l, Token r)
     if (len < 3) result.u.integer = 0;
   }
   /*}}}*/
-  else /* return < type error */ /*{{{*/
-  {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("type mismatch for relational operator"))+1),_("type mismatch for relational operator"));
-  }
-  /*}}}*/
+  else return relational_type_mismatch(l.type, r.type);
   return result;
 }
 /*}}}*/
@@ -881,12 +902,7 @@ Token tle(Token l, Token r)
     if (len < 3) result.u.integer = 0;
   }
   /*}}}*/
-  else /* result is <= type error */ /*{{{*/
-  {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("type mismatch for relational operator"))+1),_("type mismatch for relational operator"));
-  }
-  /*}}}*/
+  else return relational_type_mismatch(l.type, r.type);
   return result;
 }
 /*}}}*/
@@ -969,12 +985,7 @@ Token tge(Token l, Token r)
     if (len < 3) result.u.integer = 0;
   }
   /*}}}*/
-  else /* return >= type error */ /*{{{*/
-  {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("type mismatch for relational operator"))+1),_("type mismatch for relational operator"));
-  }
-  /*}}}*/
+  else return relational_type_mismatch(l.type, r.type);
   return result;
 }
 /*}}}*/
@@ -1058,12 +1069,7 @@ Token tgt(Token l, Token r)
       else if (r.u.location[len] < l.u.location[len]) ++result.u.integer;
     if (len < 3) result.u.integer = 0;
   }
-  /*}}}*/  else /* result is relation op type error */ /*{{{*/
-  {
-    result.type=EEK;
-    result.u.err=mystrmalloc(_("type mismatch for relational operator"));
-  }
-  /*}}}*/
+  else return relational_type_mismatch(l.type, r.type);
   return result;
 }
 /*}}}*/
@@ -1148,14 +1154,11 @@ Token teq(Token l, Token r)
     if (len < 3) result.u.integer = 0;
   }
   /*}}}*/
-  else
-  {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("type mismatch for relational operator"))+1),_("type mismatch for relational operator"));
-  }
+  else return relational_type_mismatch(l.type, r.type);
   return result;
 }
 /*}}}*/
+
 /* tabouteq  -- ~= operator */ /*{{{*/
 Token tabouteq(Token l, Token r)
 {
@@ -1195,7 +1198,7 @@ Token tabouteq(Token l, Token r)
   else
   {
     result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("type mismatch for relational operator"))+1),_("type mismatch for relational operator"));
+    result.u.err = strdup(_("Usage: ~= only compares float values"));
   }
   return result;
 }
@@ -1278,11 +1281,7 @@ Token tne(Token l, Token r)
     for (len = 0; len < 3 && l.u.location[len] == r.u.location[len]; ++len);
     if (len < 3) result.u.integer = 1;
   }
-  else
-  {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("type mismatch for relational operator"))+1),_("type mismatch for relational operator"));
-  }
+  else return relational_type_mismatch(l.type, r.type);
   return result;
 }
 /*}}}*/
diff --git a/src/common/eval.h b/src/common/eval.h
index 868602f..2b035f6 100644
--- a/src/common/eval.h
+++ b/src/common/eval.h
@@ -5,6 +5,7 @@
 
 Token tcopy(Token n);
 void tfree(Token *n);
+void tfree_protected(Token *n, const Token dontfree);
 void tvecfreetoks(Token **tvec);
 void tvecfree(Token **tvec);
 size_t tveclen(Token **tvec);
diff --git a/src/common/func.c b/src/common/func.c
index 7750673..bad5f11 100644
--- a/src/common/func.c
+++ b/src/common/func.c
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */
 #include <stdio.h>
+extern char *strdup(const char* s);
 #include <string.h>
 #include <time.h>
 
@@ -303,7 +304,9 @@ static double deg2rad(double x)
 }
 /*}}}*/
 
-typedef enum {ABSOLUTE, RELATIVE} LocConvention;
+typedef enum {ABSOLUTE, RELATIVE, EXCEL} LocConvention;
+
+static Token excel_adr_func(int argc, const Token argv[]);
 
 /* & */ /*{{{*/
 static Token adr_func(int argc, const Token argv[], LocConvention lcon)
@@ -314,20 +317,23 @@ static Token adr_func(int argc, const Token argv[], LocConvention lcon)
   size_t i;
   /*}}}*/
 
+  if (lcon == EXCEL) return excel_adr_func(argc, argv);
+
   /* asserts */ /*{{{*/
   assert(argv != (Token*)0);
   /*}}}*/
 
   LOCATION_GETS(result.u.location, upd_l);
   if (argc == 1 && argv[0].type == LOCATION)
-    if (lcon == ABSOLUTE) return argv[0];
-    else
-    {
-      LOCATION_ADD(result.u.location, argv[0].u.location);
-      return result;
-    }
+      if (lcon == ABSOLUTE) return argv[0];
+      else
+	{
+	  LOCATION_ADD(result.u.location, argv[0].u.location);
+	  return result;
+	}
   for (i = 0; i < argc && i < HYPER; ++i)
   {
+    if (argv[0].type == EEK) return argv[0];
     if (argv[i].type == INT)
       if (lcon == ABSOLUTE) result.u.location[i] = argv[i].u.integer;
       else result.u.location[i] += argv[i].u.integer;
@@ -355,9 +361,15 @@ static Token at_func(int argc, const Token argv[], LocConvention lcon)
     Token result;
     result.type = EEK;
     char *pref = _("Inside @: ");
+    if (lcon == RELATIVE) pref = _("Inside R(): ");
+    if (lcon == EXCEL) pref = _("Inside X(): ");
     result.u.err = malloc(strlen(location.u.err) + strlen(pref) + 1);
     strcpy(result.u.err, pref);
     strcat(result.u.err, location.u.err);
+    /* don't free the location if it is the same as an argument, because
+       those get freed later: */
+    for (size_t i = 0; i < argc; ++i)
+      if (argv[i].type == EEK & argv[i].u.err == location.u.err) return result;
     tfree(&location);
     return result;
   }
@@ -384,106 +396,104 @@ static Token rel_at_func(int argc, const Token argv[])
   return at_func(argc, argv, RELATIVE);
 }
 
-/* x */ /*{{{*/
-static Token x_func(int argc, const Token argv[])
+static Token excel_at_func(int argc, const Token argv[])
 {
-  /* variables */ /*{{{*/
-  Token result;
-  /*}}}*/
+  return at_func(argc, argv, EXCEL);
+}
 
-  if (argc==0)
-  /* result is currently updated x position */ /*{{{*/
-  {
-    result.type = INT;
-    result.u.integer = upd_l[X];
+#define INTPATIBLE(t) (t.type == INT || t.type == EMPTY)
+
+static Token excel_adr_func(int argc, const Token argv[])
+{
+  Token result;
+  char *usage = _("Usage: X&(THERE_LABEL, HERE_LABEL, [fix_x], [fix_y], [fix_z])");
+  if (argc < 2) {
+    result.type = EEK;
+    result.u.err = strdup(usage);
+    return result;
   }
-  /*}}}*/
-  else if (argc==1 && argv[0].type==LOCATION)
-  /* return x component of location */ /*{{{*/
+  if (argv[0].type == EEK) return argv[0];
+  if (argv[1].type == EEK) return argv[1];
+  if (argv[0].type != LOCATION || argv[1].type != LOCATION)
   {
-    result.type = INT;
-    result.u.integer = argv[0].u.location[X];
+    result.type = EEK;
+    result.u.err = strdup(usage);
+    return result;
   }
-  /*}}}*/
-  else
-  /* x type error */ /*{{{*/
+
+  LOCATION_GETS(result.u.location, upd_l);
+
+  for (Dimensions dim = X; dim < HYPER; ++dim)
   {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("Usage: x([location])"))+1),_("Usage: x([location])"));
+    int i = dim + 2;
+    bool fixed = false;
+    if (i < argc)
+    {
+      if (argv[i].type == EEK) return argv[i];
+      if (!INTPATIBLE(argv[i]))
+      {
+	result.type = EEK;
+	result.u.err = strdup(usage);
+	return result;
+      }
+      if (argv[i].type == INT && argv[i].u.integer > 0) fixed = true;
+    }
+    if (fixed) result.u.location[dim] = argv[0].u.location[dim];
+    else result.u.location[dim] +=
+	   argv[0].u.location[dim] - argv[1].u.location[dim];
+  }
+  return result;
+}
+
+/* dim_func -- common implementation of the coordinate functions */ /*{{{*/
+static Token dim_func(int argc, const Token argv[], Dimensions dim)
+{
+  Token result;
+  result.type = INT;
+  
+  if (argc == 0)
+  /* result is currently updated coordinate along dim */ /*{{{*/
+    result.u.integer = upd_l[dim];
+  /*}}}*/
+  else if (argc == 1 && argv[0].type == LOCATION)
+  /* return dim coordinate of location */ /*{{{*/
+    result.u.integer = argv[0].u.location[dim];
+  else /* type error */ /*{{{*/
+  {
+    result.type = EEK;
+    char *templ = _("Usage: %c([location])");
+    const char *DIMS = "xyz";
+    result.u.err = malloc(strlen(templ)+1);
+    sprintf(result.u.err, templ, DIMS[dim]);
   }
   /*}}}*/
   return result;
 }
+/*}}}/
+
+/* x */ /*{{{*/
+static Token x_func(int argc, const Token argv[])
+{
+  return dim_func(argc, argv, X);
+}
 /*}}}*/
 
 /* y */ /*{{{*/
 static Token y_func(int argc, const Token argv[])
 {
-  /* variables */ /*{{{*/
-  Token result;
-  /*}}}*/
-
-  if (argc==0)
-  /* result is currently updated y position */ /*{{{*/
-  {
-    result.type = INT;
-    result.u.integer = upd_l[Y];
-  }
-  /*}}}*/
-  else if (argc==1 && argv[0].type==LOCATION)
-  /* return y component of location */ /*{{{*/
-  {
-    result.type = INT;
-    result.u.integer = argv[0].u.location[Y];
-  }
-  /*}}}*/
-  else
-  /* y type error */ /*{{{*/
-  {
-    result.type=EEK;
-    result.u.err=strcpy(malloc(strlen(_("Usage: y([location])"))+1),_("Usage: y([location])"));
-  }
-  /*}}}*/
-  return result;
+  return dim_func(argc, argv, Y);
 }
 /*}}}*/
 
 /* z */ /*{{{*/
 static Token z_func(int argc, const Token argv[])
 {
-  /* variables */ /*{{{*/
-  Token result;
-  /*}}}*/
-
-  if (argc==0)
-  /* result is currently updated z position */ /*{{{*/
-  {
-    result.type = INT;
-    result.u.integer = upd_l[Z];
-  }
-  /*}}}*/
-  else if (argc == 1 && argv[0].type == LOCATION)
-  /* return z component of location */ /*{{{*/
-  {
-    result.type = INT;
-    result.u.integer = argv[0].u.location[Z];
-  }
-  /*}}}*/
-  else
-  /* result is z type error */ /*{{{*/
-  {
-    result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: z([location])"));
-  }
-  /*}}}*/
-  return result;
+  return dim_func(argc, argv, Z);
 }
 /*}}}*/
 
 typedef enum { LOG_AND, LOG_OR } LogicalFunction;
 
-#define INTPATIBLE(t) (t.type == INT || t.type == EMPTY)
-
 static Token bitwise_func(int argc, const Token argv[], LogicalFunction lop)
 {
   Token result;
@@ -495,7 +505,7 @@ static Token bitwise_func(int argc, const Token argv[], LogicalFunction lop)
     {
       result.type = EEK;
       result.u.err =
-	mystrmalloc(_("Bitwise functions operate only on integers."));
+	strdup(_("Bitwise functions operate only on integers."));
       return result;
     }
     int val = 0;
@@ -534,7 +544,7 @@ static Token e_func(int argc, const Token argv[])
   else /* result is e type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: e()"));
+    result.u.err=strdup(_("Usage: e()"));
   }
   /*}}}*/
   return result;
@@ -552,7 +562,7 @@ static Token eval_func(int argc, const Token argv[])
   /* nesting error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("nested eval()"));
+    result.u.err=strdup(_("nested eval()"));
   }
   /*}}}*/
   else if (argc==1 && argv[0].type==LOCATION)
@@ -806,12 +816,12 @@ static Token int_func(int argc, const Token argv[])
     if (s==(char*)0 || *s)
     {
       result.type=EEK;
-      result.u.err=mystrmalloc(_("int(string): invalid string"));
+      result.u.err=strdup(_("int(string): invalid string"));
     }
     else if (errno==ERANGE && (result.u.integer==LONG_MAX || result.u.integer==LONG_MIN))
     {
       result.type=EEK;
-      result.u.err=mystrmalloc(_("int(string): domain error"));
+      result.u.err=strdup(_("int(string): domain error"));
     }
     else result.type=INT;
   }
@@ -869,7 +879,7 @@ static Token len_func(int argc, const Token argv[])
   /* result is frac type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: len(string)"));
+    result.u.err=strdup(_("Usage: len(string)"));
   }
   /*}}}*/
   return result;
@@ -912,7 +922,7 @@ static Token log_func(int argc, const Token argv[])
   else /* result is log type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: log(float[,float])"));
+    result.u.err=strdup(_("Usage: log(float[,float])"));
   }
   /*}}}*/
   return result;
@@ -998,7 +1008,7 @@ static Token minmax_func(int argc, const Token argv[], int min)
   /* result is min/max type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(min ? _("Usage: min(location,location) or min(val1, val2,...)") : _("Usage: max(location,location) or max(val1,val2,...)"));
+    result.u.err=strdup(min ? _("Usage: min(location,location) or min(val1, val2,...)") : _("Usage: max(location,location) or max(val1,val2,...)"));
     return result;
   }
   /*}}}*/
@@ -1041,7 +1051,7 @@ static Token abs_func(int argc, const Token argv[])
   /* result is abs type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: abs(float|integer)"));
+    result.u.err=strdup(_("Usage: abs(float|integer)"));
   }
   /*}}}*/
   return result;
@@ -1060,12 +1070,12 @@ static Token env_func(int argc, const Token argv[])
 
     if ((e=getenv(argv[0].u.string))==(char*)0) e="";
     result.type=STRING;
-    result.u.string=mystrmalloc(e);
+    result.u.string=strdup(e);
   }
   else
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: $(string)"));
+    result.u.err=strdup(_("Usage: $(string)"));
   }
   return result;
 }
@@ -1088,7 +1098,7 @@ static Token float_func(int argc, const Token argv[])
     else
     {
       result.type=EEK;
-      result.u.err=mystrmalloc(_("Not a (finite) floating point number"));
+      result.u.err=strdup(_("Not a (finite) floating point number"));
     }
   }
   /*}}}*/
@@ -1096,7 +1106,7 @@ static Token float_func(int argc, const Token argv[])
   /* float type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: float(string)"));
+    result.u.err=strdup(_("Usage: float(string)"));
   }
   /*}}}*/
   return result;
@@ -1119,7 +1129,7 @@ static Token strftime_func(int argc, const Token argv[])
     tm=localtime(&t);
     strftime(s,sizeof(s),argv[0].u.string,tm);
     s[sizeof(s)-1]='\0';
-    result.u.string=mystrmalloc(s);
+    result.u.string=strdup(s);
     result.type=STRING;
   }
   /*}}}*/
@@ -1133,14 +1143,14 @@ static Token strftime_func(int argc, const Token argv[])
     tm=localtime(&t);
     strftime(s,sizeof(s),argv[0].u.string,tm);
     s[sizeof(s)-1]='\0';
-    result.u.string=mystrmalloc(s);
+    result.u.string=strdup(s);
     result.type=STRING;
   }
   /*}}}*/
   else /* strftime type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: strftime(string[,integer])"));
+    result.u.err=strdup(_("Usage: strftime(string[,integer])"));
   }
   /*}}}*/
   return result;
@@ -1184,7 +1194,7 @@ static Token clock_func(int argc, const Token argv[])
   else /* wrong usage */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: clock(condition,location[,location])"));
+    result.u.err=strdup(_("Usage: clock(condition,location[,location])"));
   }
   /*}}}*/
   return result;
@@ -1322,7 +1332,7 @@ static Token rnd_func(int argc, const Token argv[])
   else
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: rnd()"));
+    result.u.err=strdup(_("Usage: rnd()"));
   }
   return result;
 }
@@ -1351,7 +1361,7 @@ static Token substr_func(int argc, const Token argv[])
 	ss[n] = '\0';
 	strncpy(ss, argv[0].u.string + b, n);
 	result.type=STRING;
-	result.u.string=mystrmalloc(ss);
+	result.u.string=strdup(ss);
     }
     else {
 	result.type=EMPTY;
@@ -1360,7 +1370,7 @@ static Token substr_func(int argc, const Token argv[])
   else
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: substr(string,integer,integer)"));
+    result.u.err=strdup(_("Usage: substr(string,integer,integer)"));
   }
   return result;
 }
@@ -1387,7 +1397,7 @@ static Token strptime_func(int argc, const Token argv[])
   else /* strftime type error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: strptime(string,string)"));
+    result.u.err=strdup(_("Usage: strptime(string,string)"));
   }
   /*}}}*/
   return result;
@@ -1406,7 +1416,7 @@ static Token time_func(int argc, const Token argv[])
   else	
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Usage: time()"));
+    result.u.err=strdup(_("Usage: time()"));
   }
   return result;
 }
@@ -1460,8 +1470,10 @@ Tfunc tfunc[]=
   { "time", time_func },
   { "bitand", bitand_func },
   { "bitor", bitor_func },
-  { "D", rel_adr_func },
   { "R", rel_at_func },
+  { "D", rel_adr_func },
+  { "X", excel_at_func },
+  { "X&", excel_adr_func },
   { "", (Token (*)(int, const Token[]))0 }
 };
 /*}}}*/
diff --git a/src/common/main.c b/src/common/main.c
index 074b915..25cd801 100644
--- a/src/common/main.c
+++ b/src/common/main.c
@@ -24,6 +24,7 @@
 extern char *optarg;
 extern int optind,opterr,optopt;
 int getopt(int argc, char * const *argv, const char *optstring);
+extern char *strdup(const char* s);
 #include <string.h>
 #include <unistd.h>
 
@@ -680,9 +681,9 @@ static int do_savecsv(Sheet *cursheet, const char *name)
     MenuChoice menu[4];
     name = cursheet->name;
 
-    menu[0].str=mystrmalloc(_("cC)omma (,)")); menu[0].c='\0';
-    menu[1].str=mystrmalloc(_("sS)emicolon (;)")); menu[1].c='\0';
-    menu[2].str=mystrmalloc(_("tT)ab (\\t)")); menu[2].c='\0';
+    menu[0].str=strdup(_("cC)omma (,)")); menu[0].c='\0';
+    menu[1].str=strdup(_("sS)emicolon (;)")); menu[1].c='\0';
+    menu[2].str=strdup(_("tT)ab (\\t)")); menu[2].c='\0';
     menu[3].str=(char*)0;
     sep=line_menu(_("Choose separator:"),menu,0);
     if (sep < 0) return sep;
@@ -912,9 +913,9 @@ static int do_insert(Sheet *sheet)
   {
     MenuChoice menu[4];
 
-    menu[0].str=mystrmalloc(_("cC)olumn")); menu[0].c='\0';
-    menu[1].str=mystrmalloc(_("rR)ow")); menu[1].c='\0';
-    menu[2].str=mystrmalloc(_("dD)epth")); menu[2].c='\0';
+    menu[0].str=strdup(_("cC)olumn")); menu[0].c='\0';
+    menu[1].str=strdup(_("rR)ow")); menu[1].c='\0';
+    menu[2].str=strdup(_("dD)epth")); menu[2].c='\0';
     menu[3].str=(char*)0;
     reply=line_menu(_("Insert:"),menu,0);
     free(menu[0].str);
@@ -938,12 +939,12 @@ static int do_insert(Sheet *sheet)
     /* show menu */ /*{{{*/
     switch (reply)
     {
-      case 0: menu[0].str=mystrmalloc(_("wW)hole column")); break;
-      case 1: menu[0].str=mystrmalloc(_("wW)hole line")); break;
-      case 2: menu[0].str=mystrmalloc(_("wW)hole sheet")); break;
+      case 0: menu[0].str=strdup(_("wW)hole column")); break;
+      case 1: menu[0].str=strdup(_("wW)hole line")); break;
+      case 2: menu[0].str=strdup(_("wW)hole sheet")); break;
       default: assert(0);
     }
-    menu[1].str=mystrmalloc(_("sS)ingle cell")); menu[1].c='\0';
+    menu[1].str=strdup(_("sS)ingle cell")); menu[1].c='\0';
     menu[2].str=(char*)0;
     r=line_menu(_("Insert:"),menu,0);
     free(menu[0].str);
@@ -1029,9 +1030,9 @@ static int do_delete(Sheet *sheet)
   {
     MenuChoice menu[4];
 
-    menu[0].str=mystrmalloc(_("cC)olumn")); menu[0].c='\0';
-    menu[1].str=mystrmalloc(_("rR)ow")); menu[1].c='\0';
-    menu[2].str=mystrmalloc(_("dD)epth")); menu[2].c='\0';
+    menu[0].str=strdup(_("cC)olumn")); menu[0].c='\0';
+    menu[1].str=strdup(_("rR)ow")); menu[1].c='\0';
+    menu[2].str=strdup(_("dD)epth")); menu[2].c='\0';
     menu[3].str=(char*)0;
     reply=line_menu(_("Delete:"),menu,0);
     free(menu[0].str);
@@ -1056,12 +1057,12 @@ static int do_delete(Sheet *sheet)
     /* show menu */ /*{{{*/
     switch (reply)
     {
-      case 0: menu[0].str=mystrmalloc(_("wW)hole column")); break;
-      case 1: menu[0].str=mystrmalloc(_("wW)hole line")); break;
-      case 2: menu[0].str=mystrmalloc(_("wW)hole sheet")); break;
+      case 0: menu[0].str=strdup(_("wW)hole column")); break;
+      case 1: menu[0].str=strdup(_("wW)hole line")); break;
+      case 2: menu[0].str=strdup(_("wW)hole sheet")); break;
       default: assert(0);
     }
-    menu[1].str=mystrmalloc(_("sS)ingle cell")); menu[1].c='\0';
+    menu[1].str=strdup(_("sS)ingle cell")); menu[1].c='\0';
     menu[2].str=(char*)0;
     r=line_menu(_("Delete:"),menu,0);
     free(menu[0].str);
@@ -1233,15 +1234,15 @@ static int do_sort(Sheet *sheet)
 
   do_mark(sheet, GET_MARK_ALL);
   /* build menues */ /*{{{*/
-  menu1[0].str=mystrmalloc(_("cC)olumn"));     menu1[0].c='\0';
-  menu1[1].str=mystrmalloc(_("rR)ow"));     menu1[1].c='\0';
-  menu1[2].str=mystrmalloc(_("dD)epth"));     menu1[2].c='\0';
+  menu1[0].str=strdup(_("cC)olumn"));     menu1[0].c='\0';
+  menu1[1].str=strdup(_("rR)ow"));     menu1[1].c='\0';
+  menu1[2].str=strdup(_("dD)epth"));     menu1[2].c='\0';
   menu1[3].str=(char*)0;
-  menu2[0].str=mystrmalloc(_("sS)ort region"));  menu2[0].c='\0';
-  menu2[1].str=mystrmalloc(_("aA)dd key"));  menu2[1].c='\0';
+  menu2[0].str=strdup(_("sS)ort region"));  menu2[0].c='\0';
+  menu2[1].str=strdup(_("aA)dd key"));  menu2[1].c='\0';
   menu2[2].str=(char*)0;
-  menu3[0].str=mystrmalloc(_("aA)scending"));  menu3[0].c='\0';
-  menu3[1].str=mystrmalloc(_("dD)escending")); menu3[0].c='\0';
+  menu3[0].str=strdup(_("aA)scending"));  menu3[0].c='\0';
+  menu3[1].str=strdup(_("dD)escending")); menu3[0].c='\0';
   menu3[2].str=(char*)0;
   /*}}}*/
 
@@ -1453,9 +1454,9 @@ static int do_mirror(Sheet *sheet)
   {
     MenuChoice menu[4];
 
-    menu[0].str=mystrmalloc(_("lL)eft-right")); menu[0].c='\0';
-    menu[1].str=mystrmalloc(_("uU)pside-down")); menu[1].c='\0';
-    menu[2].str=mystrmalloc(_("fF)ront-back")); menu[2].c='\0';
+    menu[0].str=strdup(_("lL)eft-right")); menu[0].c='\0';
+    menu[1].str=strdup(_("uU)pside-down")); menu[1].c='\0';
+    menu[2].str=strdup(_("fF)ront-back")); menu[2].c='\0';
     menu[3].str=(char*)0;
     reply=line_menu(_("Mirror block:"),menu,0);
     free(menu[0].str);
@@ -1882,7 +1883,7 @@ int main(int argc, char *argv[])
   {
     const char *msg;
 
-    cursheet->name=mystrmalloc(loadfile);
+    cursheet->name=strdup(loadfile);
     if (usexdr)
     {
       if ((msg=loadxdr(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from XDR file:"),msg);
diff --git a/src/common/misc.c b/src/common/misc.c
index ef28123..ef9a3c1 100644
--- a/src/common/misc.c
+++ b/src/common/misc.c
@@ -88,12 +88,7 @@ void posorder(int *x, int *y)
   }
 }
 /*}}}*/
-/* mystrmalloc -- return malloced copy of string */ /*{{{*/
-char *mystrmalloc(const char *str)
-{
-  return (strcpy(malloc(strlen(str)+1),str));
-}
-/*}}}*/
+
 /* finite      -- return error message about number or null */ /*{{{*/
 static volatile int caughtfpe;
 
diff --git a/src/common/misc.h b/src/common/misc.h
index db0d24b..f7ed7de 100644
--- a/src/common/misc.h
+++ b/src/common/misc.h
@@ -11,7 +11,6 @@ extern "C" {
 
 void posorder(int *x, int *y);
 long int posnumber(const char *s, const char **endptr);
-char *mystrmalloc(const char *str);
 const char *dblfinite(double x);
 int fputc_close(char c, FILE *fp);
 int fputs_close(const char *s, FILE *fp);
diff --git a/src/common/parser.c b/src/common/parser.c
index 9338bf2..c9063bb 100644
--- a/src/common/parser.c
+++ b/src/common/parser.c
@@ -12,8 +12,10 @@
 
 #include <assert.h>
 #include <ctype.h>
+#include <float.h>
 #include <stdio.h>
 #include <stdlib.h>
+extern char *strdup(const char* s);
 #include <string.h>
 
 
@@ -89,7 +91,7 @@ static Token primary(Token *n[], int *i)
       /* return error, value expected */ /*{{{*/
       {
         result.type=EEK;
-        result.u.err=mystrmalloc(_("value expected"));
+        result.u.err=strdup(_("value expected"));
         return result;
       }
       /*}}}*/
@@ -153,8 +155,10 @@ static Token primary(Token *n[], int *i)
         /* eval function */ /*{{{*/
         {
           ++(*i);
-          result=tfuncall(ident,argc,argv);  
-          for (j=0; j<argc; ++j) tfree(&argv[j]);
+          result = tfuncall(ident,argc,argv);
+	  /* To allow a function to return one of its arguments, we need
+	     to be sure not to free that argument: */
+          for (j=0; j<argc; ++j) tfree_protected(&argv[j], result);
         }
         /*}}}*/
         else
@@ -171,7 +175,7 @@ static Token primary(Token *n[], int *i)
       else
       { 
         result.type=EEK;
-        result.u.err=mystrmalloc(_("( expected"));
+        result.u.err=strdup(_("( expected"));
         return result;
       }
     }
@@ -179,7 +183,7 @@ static Token primary(Token *n[], int *i)
     default: ; /* fall through */
   }
   result.type=EEK;
-  result.u.err=mystrmalloc(_("value expected"));
+  result.u.err=strdup(_("value expected"));
   return result;
 }
 /*}}}*/
diff --git a/src/common/scanner.h b/src/common/scanner.h
index 23f52a5..dbb713f 100644
--- a/src/common/scanner.h
+++ b/src/common/scanner.h
@@ -15,6 +15,7 @@ typedef enum {
 #endif
 } Type;
 
+#define MAX_TYPE_NAME_LENGTH 16
 extern const char *Type_Name[];
 
 typedef enum { PLUS, MINUS, MUL, DIV, OP, CP, COMMA, LT, LE, GE, GT, ISEQUAL, ABOUTEQ, NE, POW, MOD } Operator;
diff --git a/src/common/sheet.c b/src/common/sheet.c
index 88ab92f..32aff69 100644
--- a/src/common/sheet.c
+++ b/src/common/sheet.c
@@ -538,7 +538,9 @@ Token getvalue(Sheet *sheet, const Location at)
   /* return error */ /*{{{*/
   {
     result.type=EEK;
-    result.u.err=mystrmalloc(_("Negative index"));
+    char *templ = _("Attempt to access cell at (%d,%d,%d) with negative index");
+    result.u.err = malloc(strlen(templ)+32);
+    sprintf(result.u.err, templ, at[X], at[Y], at[Z]);
     return result;
   }
   /*}}}*/
@@ -831,7 +833,10 @@ Token findlabel(Sheet *sheet, const char *label)
   else
   {
     result.type = EEK;
-    result.u.err = mystrmalloc(_("No such label"));
+    char *pref = _("No such label: ");
+    result.u.err = malloc(strlen(pref) + strlen(label) + 1);
+    strcpy(result.u.err, pref);
+    strcat(result.u.err, label);
   }
   return result;
 }
@@ -860,7 +865,7 @@ void relabel(Sheet *sheet, const Location at,
 	  if ((*run)->type==LIDENT && strcmp((*run)->u.lident, oldlabel)==0)
 	  {
 	    free((*run)->u.lident);
-	    (*run)->u.lident = mystrmalloc(newlabel);
+	    (*run)->u.lident = strdup(newlabel);
 	  }
   cachelabels(sheet);
   forceupdate(sheet);
@@ -1337,7 +1342,7 @@ const char *loadport(Sheet *sheet, const char *name)
               ++ns;
               while (*ns && *ns!=' ') { *p=*ns; ++p; ++ns; }
               *p='\0';
-              loaded.label=mystrmalloc(buf);
+              loaded.label=strdup(buf);
               break;
             }
             /*}}}*/
@@ -1683,7 +1688,7 @@ const char *loadcsv(Sheet *sheet, const char *name)
           if (s!=cend) /* ok, it is a string */ /*{{{*/
           {
             t[0]->type=STRING;
-            t[0]->u.string=mystrmalloc(str);
+            t[0]->u.string=strdup(str);
             putcont(sheet, where, t, BASE);
           }
           /*}}}*/
diff --git a/src/common/wk1.c b/src/common/wk1.c
index 5713058..89f83ea 100644
--- a/src/common/wk1.c
+++ b/src/common/wk1.c
@@ -12,6 +12,7 @@
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
+extern char *strdup(const char *s);
 #include <string.h>
 #ifdef OLD_REALLOC
 #define realloc(s,l) myrealloc(s,l)
@@ -891,7 +892,7 @@ const char *loadwk1(Sheet *sheet, const char *name)
         t[0]=malloc(sizeof(Token));
         t[1]=(Token*)0;
         t[0]->type=STRING;
-        t[0]->u.string=mystrmalloc(body+6);
+        t[0]->u.string=strdup(body+6);
         putcont(sheet, tmp, t, BASE);
         format((unsigned char)body[0], cell);
         break;
@@ -1098,7 +1099,7 @@ const char *loadwk1(Sheet *sheet, const char *name)
         t[0]=malloc(sizeof(Token));
         t[1]=(Token*)0;
         t[0]->type=STRING;
-        t[0]->u.string=mystrmalloc(body+5);
+        t[0]->u.string=strdup(body+5);
         putcont(sheet, tmp, t, BASE);
         format((unsigned char)body[0], cell);
         break;
diff --git a/src/display.c b/src/display.c
index fed3697..51b2a0c 100644
--- a/src/display.c
+++ b/src/display.c
@@ -22,6 +22,7 @@
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+extern char *strdup(const char* s);
 #include <string.h>
 #include <unistd.h>
 #ifdef NEED_BCOPY
@@ -66,21 +67,21 @@ static int do_attribute(Sheet *cursheet)
   int c;
 
   /* create menus */
-  adjmenu[0].str=mystrmalloc(_("lL)eft"));     adjmenu[0].c='\0';
-  adjmenu[1].str=mystrmalloc(_("rR)ight"));    adjmenu[1].c='\0';
-  adjmenu[2].str=mystrmalloc(_("cC)entered")); adjmenu[2].c='\0';
-  adjmenu[3].str=mystrmalloc(_("11).23e1 <-> 12.3"));      adjmenu[3].c='\0';
-  adjmenu[4].str=mystrmalloc(_("pP)recision"));  adjmenu[4].c='\0';
-  adjmenu[5].str=mystrmalloc(_("sS)hadow"));     adjmenu[5].c='\0';
-  adjmenu[6].str=mystrmalloc(_("bB)old"));     adjmenu[6].c='\0';
-  adjmenu[7].str=mystrmalloc(_("uU)nderline"));     adjmenu[7].c='\0';
-  adjmenu[8].str=mystrmalloc(_("oO)utput special characters"));   adjmenu[8].c='\0';
+  adjmenu[0].str=strdup(_("lL)eft"));     adjmenu[0].c='\0';
+  adjmenu[1].str=strdup(_("rR)ight"));    adjmenu[1].c='\0';
+  adjmenu[2].str=strdup(_("cC)entered")); adjmenu[2].c='\0';
+  adjmenu[3].str=strdup(_("11).23e1 <-> 12.3"));      adjmenu[3].c='\0';
+  adjmenu[4].str=strdup(_("pP)recision"));  adjmenu[4].c='\0';
+  adjmenu[5].str=strdup(_("sS)hadow"));     adjmenu[5].c='\0';
+  adjmenu[6].str=strdup(_("bB)old"));     adjmenu[6].c='\0';
+  adjmenu[7].str=strdup(_("uU)nderline"));     adjmenu[7].c='\0';
+  adjmenu[8].str=strdup(_("oO)utput special characters"));   adjmenu[8].c='\0';
   adjmenu[9].str=(char*)0;
 
-  mainmenu[0].str=mystrmalloc(_("rR)epresentation"));    mainmenu[0].c='\0';
-  mainmenu[1].str=mystrmalloc(_("lL)abel"));     mainmenu[1].c='\0';
-  mainmenu[2].str=mystrmalloc(_("oLo)ck"));    mainmenu[2].c='\0';
-  mainmenu[3].str=mystrmalloc(_("iI)gnore"));  mainmenu[3].c='\0';
+  mainmenu[0].str=strdup(_("rR)epresentation"));    mainmenu[0].c='\0';
+  mainmenu[1].str=strdup(_("lL)abel"));     mainmenu[1].c='\0';
+  mainmenu[2].str=strdup(_("oLo)ck"));    mainmenu[2].c='\0';
+  mainmenu[3].str=strdup(_("iI)gnore"));  mainmenu[3].c='\0';
   mainmenu[4].str=(char*)0;
 
   do
@@ -155,9 +156,9 @@ static int do_file(Sheet *cursheet)
   int c;
 
 
-  menu[0].str=mystrmalloc(_("lL)oad")); menu[0].c='\0';
-  menu[1].str=mystrmalloc(_("sS)ave")); menu[1].c='\0';
-  menu[2].str=mystrmalloc(_("nN)ame")); menu[2].c='\0';
+  menu[0].str=strdup(_("lL)oad")); menu[0].c='\0';
+  menu[1].str=strdup(_("sS)ave")); menu[1].c='\0';
+  menu[2].str=strdup(_("nN)ame")); menu[2].c='\0';
   menu[3].str=(char*)0;
   c=0;
   do
@@ -257,14 +258,14 @@ static int do_block(Sheet *cursheet)
   MenuChoice block[9];
   int c;
 
-  block[0].str=mystrmalloc(_("ecle)ar"));  block[0].c='\0';
-  block[1].str=mystrmalloc(_("iI)nsert")); block[1].c='\0';
-  block[2].str=mystrmalloc(_("dD)elete")); block[2].c='\0';
-  block[3].str=mystrmalloc(_("mM)ove"));   block[3].c='\0';
-  block[4].str=mystrmalloc(_("cC)opy"));   block[4].c='\0';
-  block[5].str=mystrmalloc(_("fF)ill"));   block[5].c='\0';
-  block[6].str=mystrmalloc(_("sS)ort"));   block[6].c='\0';
-  block[7].str=mystrmalloc(_("rMir)ror")); block[7].c='\0';
+  block[0].str=strdup(_("ecle)ar"));  block[0].c='\0';
+  block[1].str=strdup(_("iI)nsert")); block[1].c='\0';
+  block[2].str=strdup(_("dD)elete")); block[2].c='\0';
+  block[3].str=strdup(_("mM)ove"));   block[3].c='\0';
+  block[4].str=strdup(_("cC)opy"));   block[4].c='\0';
+  block[5].str=strdup(_("fF)ill"));   block[5].c='\0';
+  block[6].str=strdup(_("sS)ort"));   block[6].c='\0';
+  block[7].str=strdup(_("rMir)ror")); block[7].c='\0';
   block[8].str=(char*)0;
   c=0;
   do
@@ -303,14 +304,14 @@ int show_menu(Sheet *cursheet)
   int c = K_INVALID;
 
 
-  menu[0].str=mystrmalloc(_("aA)ttributes"));   menu[0].c='\0';
-  menu[1].str=mystrmalloc(_("wW)idth")); menu[1].c='\0';
-  menu[2].str=mystrmalloc(_("bB)lock"));  menu[2].c='\0';
-  menu[3].str=mystrmalloc(_("fF)ile"));  menu[3].c='\0';
-  menu[4].str=mystrmalloc(_("gG)oto"));  menu[4].c='\0';
-  menu[5].str=mystrmalloc(_("sS)hell")); menu[5].c='\0';
-  menu[6].str=mystrmalloc(_("vV)ersion"));  menu[6].c='\0';
-  menu[7].str=mystrmalloc(_("qQ)uit"));   menu[7].c='\0';
+  menu[0].str=strdup(_("aA)ttributes"));   menu[0].c='\0';
+  menu[1].str=strdup(_("wW)idth")); menu[1].c='\0';
+  menu[2].str=strdup(_("bB)lock"));  menu[2].c='\0';
+  menu[3].str=strdup(_("fF)ile"));  menu[3].c='\0';
+  menu[4].str=strdup(_("gG)oto"));  menu[4].c='\0';
+  menu[5].str=strdup(_("sS)hell")); menu[5].c='\0';
+  menu[6].str=strdup(_("vV)ersion"));  menu[6].c='\0';
+  menu[7].str=strdup(_("qQ)uit"));   menu[7].c='\0';
   menu[8].str=(char*)0;
 
   do
@@ -838,8 +839,8 @@ int line_ok(const char *prompt, int curx)
 
   assert(curx==0 || curx==1);
 
-  menu[0].str=mystrmalloc(_("nN)o")); menu[0].c='\0';
-  menu[1].str=mystrmalloc(_("yY)es")); menu[1].c='\0';
+  menu[0].str=strdup(_("nN)o")); menu[0].c='\0';
+  menu[1].str=strdup(_("yY)es")); menu[1].c='\0';
   menu[2].str=(char*)0;
   result=line_menu(prompt,menu,curx);
   free(menu[0].str);