Implement a relative reference robust to source and reference moving

The new function is X(SRC,REF), which briefly for a cell in the neighborhood
   of REF returns the corresponding cell in the neighborhood in SRC. For
   further details, see the updated documention.

   In developing and documenting this function, I refined some of the
   existing error messages (e.g. showing the coordinates when there is an
   attempt to obtain a cell with a negative coordinate) and improved the
   error propagation to increase the chance that the innermost error will
   percolate to the top level.
This commit is contained in:
Glen Whitney 2019-07-30 15:07:54 -04:00
parent ab1a2fbb5a
commit 341b12ba04
12 changed files with 650 additions and 238 deletions

View File

@ -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

View File

@ -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;
}
/*}}}*/

View File

@ -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);

View File

@ -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 }
};
/*}}}*/

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
/*}}}*/

View File

@ -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;

View File

@ -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);
}
/*}}}*/

View File

@ -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;

View File

@ -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);