teapot-spreadsheet/src/common/main.c

1877 lines
50 KiB
C

/* #includes */ /*{{{C}}}*//*{{{*/
#ifndef NO_POSIX_SOURCE
#undef _POSIX_SOURCE
#define _POSIX_SOURCE 1
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#include <assert.h>
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
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>
#include "default.h"
#include "display.h"
#include "eval.h"
#include "htmlio.h"
#include "latex.h"
#include "context.h"
#include "main.h"
#include "misc.h"
#include "sc.h"
#include "scanner.h"
#include "utf8.h"
#include "parser.h"
#include "sheet.h"
#include "wk1.h"
#include "xdr.h"
/*}}}*/
/* variables */ /*{{{*/
char helpfile[PATH_MAX];
bool batch = false;
unsigned int batchln=0;
int def_precision = DEF_PRECISION;
StringFormat quote = DIRECT_STRING;
bool header = true;
bool always_redraw = false;
int debug_level = 0;
static bool usexdr = false;
/*}}}*/
void moveto(Sheet *sheet, int x, int y, int z)
{
int need_redraw = 0;
int xdir = x > sheet->cur[X]?1:-1;
if (x >= 0) sheet->cur[X] = x;
if (y >= 0) sheet->cur[Y] = y;
if (z >= 0) need_redraw++, sheet->cur[Z] = z;
while (sheet->cur[X] > 0 && SHADOWED(sheet, sheet->cur)) sheet->cur[X] += xdir;
if (getmarkstate(sheet) == MARKING) LOCATION_GETS(sheet->mark2, sheet->cur);
if (sheet->cur[X] <= sheet->offx && sheet->offx) need_redraw++, sheet->offx = (sheet->cur[X]?sheet->cur[X]-1:0);
if (sheet->cur[Y] <= sheet->offy && sheet->offy) need_redraw++, sheet->offy = (sheet->cur[Y]?sheet->cur[Y]-1:0);
if (sheet->cur[X] >= sheet->offx+sheet->maxx) need_redraw++, sheet->offx = sheet->cur[X]-sheet->maxx+2;
if (sheet->cur[Y] >= sheet->offy+sheet->maxy) need_redraw++, sheet->offy = sheet->cur[Y]-sheet->maxy+2;
if (need_redraw) redraw_sheet(sheet);
else if (x != sheet->cur[X] || y != sheet->cur[Y] || z != sheet->cur[Z]) redraw_cell(sheet, sheet->cur);
}
void movetoloc(Sheet *sheet, const Location dest) {
moveto(sheet, dest[X], dest[Y], dest[Z]);
}
void relmoveto(Sheet *sheet, int x, int y, int z)
{
moveto(sheet, sheet->cur[X]+x, sheet->cur[Y]+y, (z?sheet->cur[Z]+z:-1));
}
/* line_numedit -- number line editor function */ /*{{{*/
static int line_numedit(int *n, const char *prompt)
{
assert(prompt != NULL);
char buf[20];
Token *t = NULLTOKEN;
sprintf(buf, "%d", *n);
char *s = buf+strlen(buf);
do
{
free(t);
size_t x = s - buf;
size_t offx = 0;
int c = line_edit((Sheet*)0, buf, sizeof(buf), prompt, &x, &offx);
if (c < 0) return c;
s = buf;
t = scan_integer(&s);
} while (*s != '\0');
if (t == NULLTOKEN) *n = -1;
else { *n = t->u.integer; free(t); }
return 0;
}
/*}}}*/
/* line_lidedit -- label identifier line editor function */ /*{{{*/
static int line_idedit(char *ident, size_t size, const char *prompt, size_t *x, size_t *offx)
{
Token *t = NULLTOKEN;
char *s = ident+strlen(ident);
do
{
free(t);
*x = s - ident;
int c = line_edit((Sheet*)0, ident, size, prompt, x, offx);
if (c < 0) return c;
s = ident;
t = scan_ident(&s);
} while (*s != '\0' || t == NULLTOKEN || t->type != LIDENT);
free(t);
return 0;
}
/*}}}*/
/* doanyway -- ask if action should be done despite unsaved changes */ /*{{{*/
int doanyway(Sheet *sheet, const char *msg)
{
int result;
if (sheet->changed) {
result = line_ok(msg,0);
if (result < 0) return 0;
return result;
}
return 1;
}
/*}}}*/
/* do_edit -- set or modify cell contents */ /*{{{*/
static int do_edit(Sheet *cursheet, Key c, char *expr, TokVariety tv)
{
/* variables */ /*{{{*/
char buf[1024];
const char *prompt;
char *s;
size_t x,offx;
Location scur;
Token **t;
Token newcont;
Cell *cell;
/*}}}*/
cell = curcell(cursheet);
if (locked(cell)) line_msg(_("Edit cell:"),_("Cell is locked"));
else
{
newcont.type = EMPTY;
LOCATION_GETS(scur, cursheet->cur);
if (expr)
{
s = expr;
t = scan(&s);
prompt = _("Cell contents:");
if (tv == ITER_CONT) prompt = _("Clocked cell contents");
if (*s != '\0')
if (t == EMPTY_TVEC)
line_msg(prompt, "XXX invalid expression");
else
{
newcont = eval_safe(t, LITERAL);
if (newcont.type = EEK)
line_msg(prompt, "XXX unparseable expression");
}
tvecfree(t);
}
else
{
Token cntt;
offx=0;
if (c == K_NONE)
{
cntt = gettok(cell, tv);
printtok(buf, sizeof(buf), 0, QUOTE_STRING, FLT_COMPACT, 0,
TRUNCATED_ERROR, &cntt);
s = buf+strlen(buf);
}
else if (c == K_BACKSPACE)
{
cntt = gettok(cell, tv);
printtok(buf, sizeof(buf), 0, QUOTE_STRING, FLT_COMPACT, 0,
TRUNCATED_ERROR, &cntt);
if (strlen(buf)) *mbspos(buf+strlen(buf),-1)='\0';
s = buf+strlen(buf);
}
else if (c == K_DC)
{
cntt = gettok(cell, tv);
printtok(buf, sizeof(buf), 0, QUOTE_STRING, FLT_COMPACT, 0,
TRUNCATED_ERROR, &cntt);
memmove(buf,mbspos(buf,1),strlen(mbspos(buf,1))+1);
s = buf;
}
else if (isalpha(c))
{
buf[0] = '"';
buf[1] = c;
buf[2] = 0;
s=buf+2;
}
else
{
if (c < 256) buf[0]=c;
else buf[0] = 0;
buf[1]='\0';
s=buf+1;
}
do
{
int r;
tfree(&newcont);
newcont.type = EEK;
newcont.u.err = (char *)0;
x = mbslen(buf)-mbslen(s);
prompt = _("Cell contents:");
if (tv == ITER_CONT) prompt = _("Clocked cell contents:");
if ((r = line_edit(cursheet, buf, sizeof(buf), prompt, &x, &offx)) < 0)
return r;
s = buf;
if (buf[0] == '"' && buf[strlen(buf)-1] != '"'
&& strlen(buf)+1 < sizeof(buf))
{
buf[strlen(buf)+1] = 0;
buf[strlen(buf)] = '"';
}
t = scan(&s);
if (t != EMPTY_TVEC) {
newcont = eval_safe(t, LITERAL);
}
tvecfree(t);
} while (*s != '\0' && newcont.type == EEK);
}
movetoloc(cursheet, scur);
puttok(cursheet, cursheet->cur, newcont, tv);
forceupdate(cursheet);
}
return 0;
}
/*}}}*/
/* do_label -- modify cell label */ /*{{{*/
static int do_label(Sheet *sheet)
{
/* variables */ /*{{{*/
char buf[1024],oldlabel[1024];
size_t edx,offx,ok;
Token t;
Location w;
int tried;
int c;
/*}}}*/
assert(sheet != (Sheet*)0);
do_mark(sheet, GET_MARK_ALL);
if (SAME_LOC(sheet->mark1,sheet->mark2) &&
locked(CELL_AT(sheet, sheet->mark1)))
{
line_msg(_("Cell label:"),_("Cell is locked"));
}
else
{
ok=edx=offx=0;
(void)strcpy(buf, getlabel(curcell(sheet)));
(void)strcpy(oldlabel, buf);
tried = 0;
do
{
if (tried) {
line_msg(_("Cell label:"), _("Label already in use"));
}
if ((c=line_idedit(buf,sizeof(buf),_("Cell label:"),&edx,&offx))<0) return c;
tried = 1;
if (buf[0]=='\0') ok=1;
else
{
ok=((t=findlabel(sheet,buf)).type==EEK ||
(t.type == LOCATION && SAME_LOC(t.u.location, sheet->cur)));
tfree(&t);
}
} while (!ok);
setlabel(sheet, sheet->cur, buf,1);
if (buf[0] != '\0' && oldlabel[0] != '\0')
for (ALL_LOCS_IN_REGION(sheet,w))
relabel(sheet, w, oldlabel, buf);
cachelabels(sheet);
forceupdate(sheet);
}
return -1;
}
/*}}}*/
/* do_columnwidth -- set the column width */ /*{{{*/
static int do_columnwidth(Sheet *cursheet)
{
do_mark(cursheet, GET_MARK_CUR);
int n = columnwidth(cursheet, cursheet->mark1[X], cursheet->mark1[Z]);
do {
int c = line_numedit(&n, _("Column width:"));
if (c < 0) return c;
} while (n <= 0);
/*}}}*/
for (int x = cursheet->mark1[X]; x <= cursheet->mark2[X]; ++x)
for (int z = cursheet->mark1[Z]; z <= cursheet->mark2[Z]; ++z)
setwidth(cursheet,x,z,n);
return -1;
}
/*}}}*/
/* do_attribute -- set cell attributes */ /*{{{*/
static void do_attribute(Sheet *cursheet, Key action)
{
Location w;
Adjust adj = LEFT;
FloatFormat ff = FLT_DECIMAL;
do_mark(cursheet, GET_MARK_CUR);
bool onecell = SAME_LOC(cursheet->mark1, cursheet->mark2);
Cell *fcell = safe_cell_at(cursheet, cursheet->mark1);
bool changed = false;
if (action != ADJUST_LOCK && onecell && locked(fcell))
{
line_msg(_("Cell attribute:"),_("Cell is locked"));
return;
}
switch ((int)action)
{
case ADJUST_CENTER: ++adj; /* drop through */
case ADJUST_RIGHT: ++adj; /* drop through */
case ADJUST_LEFT:
{
const char *templ = _("Change adjustment of block to %s?");
char *prompt = malloc(strlen(templ) + MAX_ADJUST_NAME_LENGTH + 1);
sprintf(prompt, templ, Adjust_Name[adj]);
if (!onecell && line_ok(prompt, 0) <= 0) break;
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = setadjust(cursheet, w, adj) || changed;
break;
}
/* set float format */ /*{{{*/
case ADJUST_HEXACT: ++ff; /* drop through */
case ADJUST_COMPACT: ++ff; /* drop through */
case ADJUST_SCIENTIFIC: ++ff; /* drop through */
case ADJUST_DECIMAL:
{
const char* templ = _("Change float format of block to %s?");
char *prompt = malloc(strlen(templ) + MAX_FLOATFORM_NAME_LENGTH +1);
sprintf(prompt, templ, FloatFormat_Name[ff]);
if (!onecell && line_ok(prompt, 0) <= 0) break;
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = setfltformat(cursheet, w, ff) || changed;
break;
}
/*}}}*/
/* 5 -- set precision */ /*{{{*/
case ADJUST_PRECISION:
{
size_t ex,offx;
int n;
offx=0;
ex=0;
n = getprecision(fcell);
const char* prompt = _("Precision for block:");
if (onecell) prompt = _("Precision for cell:");
int c = line_numedit(&n, prompt);
if (c < 0) return;
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = setprecision(cursheet, w, n) || changed;
break;
}
/*}}}*/
/* 6 -- shadow */ /*{{{*/
case ADJUST_SHADOW:
{
int n;
Location r;
LOCATION_GETS(r, cursheet->mark1); ++r[X];
if (onecell) n = !SHADOWED(cursheet, r);
else n = line_binary(_("Set block to:"),
_("uU)nshadowed"), _("sS)hadowed"),
!SHADOWED(cursheet, r));
if (cursheet->mark1[X] == 0 && n == 1) {
line_msg(_("Shadow cell:"),_("You can not shadow cells in column 0"));
break;
}
if (n >= 0) {
for (ALL_LOCS_IN_REGION(cursheet,w))
{
Location r;
if (n == 0)
{
LOCATION_GETS(r, w);
if (!SHADOWED(cursheet, r)) ++(r[X]);
for (; SHADOWED(cursheet, r); ++(r[X]))
changed = shadow(cursheet, r, false) || changed;
}
else if (w[X]>0) changed = shadow(cursheet, w, true) || changed;
}
}
if (n>0) do_mark(cursheet, UNMARKED);
break;
}
/*}}}*/
/* 7 -- transparent */ /*{{{*/
case ADJUST_TRANSPARENT:
{
int n;
if (onecell) n = !transparent(fcell);
else n = line_binary(_("Set block to:"),
_("pP)rotected"), _("tT)ransparent:"),
!transparent(fcell));
if (n >= 0)
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = maketrans(cursheet, w, n) || changed;
break;
}
/*}}}*/
/* 8 -- bold */ /*{{{*/
case ADJUST_BOLD:
{
int n;
if (onecell) n = !isbold(fcell);
else n = line_binary(_("Set block weight to:"),
_("rR)egular"), _("bB)old"), !isbold(fcell));
if (n >= 0)
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = bold(cursheet, w, n) || changed;
break;
}
/*}}}*/
/* 9 -- underline */ /*{{{*/
case ADJUST_UNDERLINE:
{
int n;
if (onecell) n = !underlined(fcell);
else n = line_binary(_("Set block to:"), _("nN)ot underline"),
_("uU)nderline"), !underlined(fcell));
if (n >= 0)
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = underline(cursheet, w, n) || changed;
break;
}
/*}}}*/
/* 1 -- edit label and goto end */ /*{{{*/
case ADJUST_LABEL:
{
do_label(cursheet);
return;
}
/*}}}*/
/* 2 -- lock */ /*{{{*/
case ADJUST_LOCK:
{
int n;
if (onecell) n = !locked(fcell);
else n = line_binary(_("Set block to:"), _("uU)nlocked"), _("lL)ocked"),
!locked(fcell));
if (n >= 0)
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = lockcell(cursheet, w, n) || changed;
break;
}
/*}}}*/
/* 3 -- ignore */ /*{{{*/
case ADJUST_IGNORE:
{
int n;
if (onecell) n = !ignored(fcell);
else n = line_binary(_("Set block to:"), _("cC)omputed"), _("iI)gnored"),
!ignored(fcell));
if (n >= 0)
for (ALL_LOCS_IN_REGION(cursheet,w))
changed = igncell(cursheet, w, n) || changed;
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default: assert(0);
/*}}}*/
}
if (changed) {
if (onecell) redraw_cell(cursheet, cursheet->mark1);
else redraw_sheet(cursheet);
forceupdate(cursheet);
}
}
/*}}}*/
/* do_saveport -- save sheet as portable ASCII file */ /*{{{*/
static int do_saveport(Sheet *cursheet, const char *name)
{
char buf[PATH_MAX];
const char *msg;
unsigned int count;
if (!name) name = cursheet->name;
if ((msg = saveport(cursheet, name, &count))) {
line_msg(_("Save sheet to ASCII file:"),msg);
return -2;
}
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save sheet to ASCII file:"),buf);
return -1;
}
/*}}}*/
/* do_savetbl -- save sheet as tbl file */ /*{{{*/
static int do_savetbl(Sheet *cursheet, const char *name)
{
char buf[PATH_MAX];
const char *msg;
int standalone=0;
unsigned int count;
do_mark(cursheet, GET_MARK_ALL);
if (!name) {
name = cursheet->name;
if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
}
if ((msg = savetbl(cursheet, name, !standalone,
cursheet->mark1, cursheet->mark2, &count))) {
line_msg(_("Save in tbl format to file:"),msg);
return -2;
}
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save in tbl format to file:"), buf);
return -1;
}
/*}}}*/
/* do_savelatex -- save sheet as LaTeX file */ /*{{{*/
static int do_savelatex(Sheet *cursheet, const char *name)
{
char buf[PATH_MAX];
const char *msg;
int standalone=0;
unsigned int count;
do_mark(cursheet, GET_MARK_ALL);
if (!name) {
name = cursheet->name;
if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
}
if ((msg = savelatex(cursheet, name, !standalone,
cursheet->mark1, cursheet->mark2, &count))) {
line_msg(_("Save in LaTeX format to file:"),msg);
return -2;
}
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save in LaTeX format to file:"), buf);
return -1;
}
/*}}}*/
/* do_savecontext -- save sheet as ConTeXt file */ /*{{{*/
static int do_savecontext(Sheet *cursheet, const char *name)
{
char buf[PATH_MAX];
const char *msg;
int standalone=0;
int x1,y1,z1,x2,y2,z2;
unsigned int count;
do_mark(cursheet, GET_MARK_ALL);
if (!name)
{
name = cursheet->name;
if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
}
if ((msg = savecontext(cursheet, name, !standalone,
cursheet->mark1, cursheet->mark2, &count)))
{
line_msg(_("Save in ConTeXt format to file:"),msg);
return -2;
}
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save in ConTeXt format to file:"), buf);
return -1;
}
/*}}}*/
/* do_savehtml -- save sheet as HTML file */ /*{{{*/
static int do_savehtml(Sheet *cursheet, const char *name)
{
char buf[PATH_MAX];
const char *msg;
int standalone=0;
int x1,y1,z1,x2,y2,z2;
unsigned int count;
do_mark(cursheet, GET_MARK_ALL);
if (!name) {
name = cursheet->name;
if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
}
if ((msg = savehtml(cursheet, name, !standalone,
cursheet->mark1, cursheet->mark2, &count))) {
line_msg(_("Save in HTML format to file:"),msg);
return -2;
}
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save in HTML format to file:"), buf);
return -1;
}
/*}}}*/
/* do_savetext -- save sheet as formatted text file */ /*{{{*/
static int do_savetext(Sheet *cursheet, const char *name)
{
char buf[PATH_MAX];
const char *msg;
int x1,y1,z1,x2,y2,z2;
unsigned int count;
do_mark(cursheet, GET_MARK_ALL);
if (!name) name = cursheet->name;
if ((msg = savetext(cursheet, name,
cursheet->mark1, cursheet->mark2, &count))) {
line_msg(_("Save in plain text format to file:"),msg);
return -2;
}
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save in plain text format to file:"), buf);
return -1;
}
/*}}}*/
/* do_savecsv -- save sheet as CSV file */ /*{{{*/
static int do_savecsv(Sheet *cursheet, const char *name)
{
do_mark(cursheet, GET_MARK_ALL);
if (!name) name = cursheet->name;
const char *menu[] =
{ _("cC)omma (,)"), _("sS)emicolon (;)"), _("tT)ab (\\t)"), NULL };
const char seps[4] = ",;\t";
int sep = line_menu(_("Choose separator:"), menu, 0);
if (sep < 0) return sep;
unsigned int count;
const char *msg;
if ((msg = savecsv(cursheet, name, seps[sep],
cursheet->mark1, cursheet->mark2, &count)))
{
line_msg(_("Save in CSV format to file:"), msg);
return -2;
}
char buf[1024];
snprintf(buf, sizeof(buf), _("%u cells written"), count);
if (!batch) line_msg(_("Save in CSV format to file:"), buf);
return -1;
}
/*}}}*/
/* do_loadxdr -- load sheet from XDR file */ /*{{{*/
static int do_loadxdr(Sheet *cursheet)
{
const char *msg;
if ((msg=loadxdr(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from XDR file:"),msg);
return -1;
}
/*}}}*/
/* do_loadport -- load sheet from portable ASCII file */ /*{{{*/
static int do_loadport(Sheet *cursheet)
{
const char *msg;
/*}}}*/
if ((msg=loadport(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from ASCII file:"),msg);
return -1;
}
/*}}}*/
/* do_loadsc -- load sheet from SC file */ /*{{{*/
static int do_loadsc(Sheet *cursheet)
{
const char *msg;
if ((msg=loadsc(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from SC file:"),msg);
return -1;
}
/*}}}*/
/* do_loadwk1 -- load sheet from WK1 file */ /*{{{*/
static int do_loadwk1(Sheet *cursheet)
{
const char *msg;
if ((msg=loadwk1(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from WK1 file:"),msg);
return -1;
}
/*}}}*/
/* do_loadcsv -- load/merge sheet from CSV file */ /*{{{*/
static int do_loadcsv(Sheet *cursheet)
{
const char *msg;
if ((msg=loadcsv(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from CSV file:"),msg);
return -1;
}
/*}}}*/
/* do_mark -- set mark */ /*{{{*/
void do_mark(Sheet *cursheet, MarkState ms)
{
MarkState ps;
Dimensions dim;
ps = getmarkstate(cursheet);
if (ms == MARK_CYCLE)
{
if (ps == UNMARKED) ms = MARKING;
else ms = ps + 1;
}
if (ps == ms) return;
switch (ms)
{
case MARKING:
{
LOCATION_GETS(cursheet->mark1, cursheet->cur);
LOCATION_GETS(cursheet->mark2, cursheet->cur);
break;
}
case UNMARKED:
break;
case MARKED:
case GET_MARK_CUR:
case GET_MARK_ALL:
{
switch (ps)
{
case MARKED: {
ms = MARKED;
break;
}
case MARKING:
{
for (dim = X; dim < HYPER; ++dim)
posorder(&(cursheet->mark1[dim]), &(cursheet->mark2[dim]));
ms = MARKED;
break;
}
case UNMARKED:
{
if (ms == GET_MARK_CUR)
{
LOCATION_GETS(cursheet->mark1, cursheet->cur);
LOCATION_GETS(cursheet->mark2, cursheet->cur);
ms = UNMARKED;
}
else if (ms == GET_MARK_ALL)
{
OLOCATION(cursheet->mark1);
cursheet->mark2[X] = cursheet->dimx - 1;
cursheet->mark2[Y] = cursheet->dimy - 1;
cursheet->mark2[Z] = cursheet->dimz - 1;
ms = UNMARKED;
}
else assert(ms == MARKED);
break;
}
default: assert(0);
}
break;
}
default: assert(0);
}
cursheet->marking = ms;
}
/*}}}*/
static int do_name(Sheet *cursheet);
/* do_save -- save sheet */ /*{{{*/
static int do_save(Sheet *cursheet)
{
const char *ext = cursheet->name;
if (ext==(char*)0) return do_name(cursheet);
ext += strlen(ext)-1;
if (!strcmp(ext-3, ".tbl")) return do_savetbl(cursheet, NULL);
if (!strcmp(ext-5, ".latex")) return do_savelatex(cursheet, NULL);
if (!strcmp(ext-4, ".html")) return do_savehtml(cursheet, NULL);
if (!strcmp(ext-3, ".csv")) return do_savecsv(cursheet, NULL);
if (!strcmp(ext-3, ".txt")) return do_savetext(cursheet, NULL);
if (!strcmp(ext-3, ".tex")) return do_savecontext(cursheet, NULL);
return do_saveport(cursheet, NULL);
}
/*}}}*/
/* do_name -- (re)name sheet */ /*{{{*/
static int do_name(Sheet *cursheet)
{
const char *name;
name = line_file(cursheet->name, _("Teapot ASCII \t*.tpa\ntbl \t*.tbl\nLaTeX \t*.latex\nHTML \t*.html\nCSV \t*.csv\nFormatted ASCII \t*.txt\nConTeXt \t*.tex"), _("New file name:"), 1);
if (!name) return -1;
if (cursheet->name!=(char*)0) free(cursheet->name);
cursheet->name=strdup(name);
return do_save(cursheet);
}
/*}}}*/
/* do_load -- load sheet */ /*{{{*/
static int do_load(Sheet *cursheet)
{
const char *name, *ext;
if (doanyway(cursheet, _("Sheet modified, load new file anyway?")) != 1) return -1;
name = line_file(cursheet->name,
_("Teapot ASCII \t*.tpa\n"
"CSV \t*.csv\n"
"Legacy binary Teapot \t*.tp\n"
"SC Spreadsheet Calculator \t*.sc\n"
"Lotus 1-2-3 \t*.wk1\n"
), _("Load sheet:"), 0);
if (!name) return -1;
if (cursheet->name!=(char*)0) free(cursheet->name);
cursheet->name=strdup(name);
ext = name+strlen(name)-1;
if (!strcmp(ext-2, ".tp")) return do_loadxdr(cursheet);
if (!strcmp(ext-2, ".sc")) return do_loadsc(cursheet);
if (!strcmp(ext-3, ".wk1")) return do_loadwk1(cursheet);
if (!strcmp(ext-3, ".csv")) return do_loadcsv(cursheet);
return do_loadport(cursheet);
}
/*}}}*/
/* do_clear -- clear block */ /*{{{*/
static int do_clear(Sheet *sheet)
{
/* variables */ /*{{{*/
Location w;
bool onecell;
int c;
/*}}}*/
do_mark(sheet, GET_MARK_CUR);
onecell = SAME_LOC(sheet->mark1, sheet->mark2);
if (onecell && locked(CELL_AT(sheet, sheet->mark1)))
line_msg(_("Clear cell:"),_("Cell is locked"));
else
{
if (!onecell)
{
if ((c=line_ok(_("Clear block:"),0))<0) return c;
else if (c!=1) return -1;
}
for (ALL_LOCS_IN_REGION(sheet, w)) freecellofsheet(sheet, w);
cachelabels(sheet);
forceupdate(sheet);
}
return -1;
}
/*}}}*/
/* do_insert -- insert block */ /*{{{*/
static int do_insert(Sheet *sheet)
{
int reply;
/* ask for direction of insertion */ /*{{{*/
{
const char* menu[] =
{ _("cC)olumn"), _("rR)ow"), _("dD)epth"), NULL };
reply = line_menu(_("Insert:"), menu, 0);
if (reply<0) return reply;
}
/*}}}*/
do_mark(sheet, GET_MARK_CUR);
Location beg, end;
LOCATION_GETS(beg, sheet->mark1);
LOCATION_GETS(end, sheet->mark2);
bool onecell = SAME_LOC(beg, end);
if (onecell)
/* ask if current cell or whole dimension should be used */ /*{{{*/
{
const char *menu[3];
/* show menu */ /*{{{*/
switch (reply)
{
case 0: menu[0] = _("wW)hole column"); break;
case 1: menu[0] = _("wW)hole line"); break;
case 2: menu[0] = _("wW)hole sheet"); break;
default: assert(0);
}
menu[1] = _("sS)ingle cell");
menu[2] = NULL;
int r = line_menu(_("Insert:"), menu, 0);
/*}}}*/
switch (r)
{
/* 0 -- use whole dimension */ /*{{{*/
case 0:
{
switch (reply)
{
/* 0 -- use whole column */ /*{{{*/
case 0:
{
beg[Y] = 0; end[Y] = sheet->dimy;
break;
}
/*}}}*/
/* 1 -- use whole line */ /*{{{*/
case 1:
{
beg[X] = 0; end[X] = sheet->dimx;
break;
}
/*}}}*/
/* 2 -- use whole layer */ /*{{{*/
case 2:
{
beg[X] = 0; end[X] = sheet->dimx;
beg[Y] = 0; end[Y] = sheet->dimy;
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default: assert(0);
/*}}}*/
}
break;
}
/*}}}*/
/* 1 -- use current cell */ /*{{{*/
case 1: break;
/*}}}*/
/* -2,-1 -- go up or abort */ /*{{{*/
case -2:
case -1: return r;
/*}}}*/
/* default -- should not happen */ /*{{{*/
default: assert(0);
/*}}}*/
}
}
/*}}}*/
/*}}}*/
switch (reply)
{
/* 0 -- columns */ /*{{{*/
case 0: insertcube(sheet, beg, end, IN_X); break;
/*}}}*/
/* 1 -- rows */ /*{{{*/
case 1: insertcube(sheet, beg, end, IN_Y); break;
/*}}}*/
/* 2 -- depth */ /*{{{*/
case 2: insertcube(sheet, beg, end, IN_Z); break;
/*}}}*/
}
return 0;
}
/*}}}*/
/* do_delete -- delete block */ /*{{{*/
static int do_delete(Sheet *sheet)
{
int reply;
firstmenu:
/* ask for direction of deletion */ /*{{{*/
{
const char* menu[] =
{ _("cC)olumn"), _("rR)ow"), _("dD)epth"), NULL };
reply = line_menu(_("Delete:"), menu, 0);
if (reply < 0) return reply;
}
/*}}}*/
do_mark(sheet, GET_MARK_CUR);
Location beg, end;
LOCATION_GETS(beg, sheet->mark1);
LOCATION_GETS(end, sheet->mark2);
bool onecell = SAME_LOC(beg, end);
if (onecell)
/* ask if range is the current cell or whole dimension should be used */ /*{{{*/
{
const char *menu[3];
/* show menu */ /*{{{*/
switch (reply)
{
case 0: menu[0] = _("wW)hole column"); break;
case 1: menu[0] = _("wW)hole line"); break;
case 2: menu[0] = _("wW)hole sheet"); break;
default: assert(0);
}
menu[1] = _("sS)ingle cell");
menu[2] = NULL;
int r = line_menu(_("Delete:"), menu, 0);
/*}}}*/
switch (r)
{
/* 0 -- use whole dimension */ /*{{{*/
case 0:
{
switch (reply)
{
/* 0 -- use whole column */ /*{{{*/
case 0:
{
beg[Y] = 0; end[Y] = sheet->dimy;
break;
}
/*}}}*/
/* 1 -- use whole line */ /*{{{*/
case 1:
{
beg[X] = 0; end[X] = sheet->dimx;
break;
}
/*}}}*/
/* 2 -- use whole layer */ /*{{{*/
case 2:
{
beg[X] = 0; end[X] = sheet->dimx;
beg[Y] = 0; end[Y] = sheet->dimy;
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default: assert(0);
/*}}}*/
}
break;
}
/*}}}*/
/* 1 -- use current cell */ /*{{{*/
case 1: break;
/*}}}*/
/* -1 -- abort */ /*{{{*/
case -1: return -1;
/*}}}*/
/* -2 -- go back to previous menu */ /*{{{*/
case -2: goto firstmenu;
/*}}}*/
/* default -- should not happen */ /*{{{*/
default: assert(0);
/*}}}*/
}
}
/*}}}*/
/*}}}*/
switch(reply)
{
/* 0 -- columns */ /*{{{*/
case 0: deletecube(sheet, beg, end, IN_X); break;
/*}}}*/
/* 1 -- rows */ /*{{{*/
case 1: deletecube(sheet, beg, end, IN_Y); break;
/*}}}*/
/* 2 -- depth */ /*{{{*/
case 2: deletecube(sheet, beg, end, IN_Z); break;
/*}}}*/
}
return -1;
}
/*}}}*/
/* do_move -- copy or move a block */ /*{{{*/
static int do_move(Sheet *sheet, int copy, int force)
{
int c;
c=-1;
if (getmarkstate(sheet) == UNMARKED) line_msg(copy ? _("Copy block:") : _("Move block:"),_("No block marked"));
else if (force || (c=line_ok(copy ? _("Copy block:") : _("Move block:"),0))==1)
{
do_mark(sheet, MARKED);
moveblock(sheet, sheet->mark1, sheet->mark2, sheet->cur, copy);
if (!copy) sheet->marking = UNMARKED;
}
if (c<0) return c; else return -1;
}
/*}}}*/
/* do_fill -- fill a block */ /*{{{*/
static int do_fill(Sheet *sheet)
{
/* variables */ /*{{{*/
int cols,rows,layers;
int x,y,z;
Location wid, go;
int c;
/*}}}*/
if (getmarkstate(sheet) == UNMARKED) line_msg(_("Fill block:"),_("No block marked"));
else
{
do_mark(sheet, MARKED);
cols=rows=layers=1;
firstmenu:
do {
int c = line_numedit(&cols, _("Number of column-wise repetitions:"));
if (c < 0) return c;
} while (cols <= 0);
secondmenu:
do
{
int c = line_numedit(&rows, _("Number of row-wise repetitions:"));
if (c == -1) return -1;
else if (c == -2) goto firstmenu;
} while (rows <= 0);
do
{
int c = line_numedit(&layers, _("Number of depth-wise repetitions:"));
if (c == -1) return -1;
else if (c == -2) goto secondmenu;
} while (layers <= 0);
LOCATION_GETS(wid, sheet->mark2);
LOCATION_SUB(wid, sheet->mark1);
wid[X]++; wid[Y]++; wid[Z]++;
LOCATION_GETS(go, sheet->cur);
for (z=0; z<layers; ++z, go[Z] += wid[Z])
for (y=0, go[Y] = sheet->cur[Y]; y<rows; ++y, go[Y] += wid[Y])
for (x=0, go[X] = sheet->cur[X]; x<cols; ++x, go[X] += wid[X])
moveblock(sheet, sheet->mark1, sheet->mark2, go, 1);
}
return -1;
}
/*}}}*/
/* fill the entire marked region using the current cell */
void fillwith(Sheet *she)
{
do_mark(she, GET_MARK_ALL);
Cell *src = curcell(she);
Location dest;
bool scan_labels = (src != NULLCELL && getlabel(src) != (const char*)0);
/* the following is safe since we have handled copying a cell to itself */
for (ALL_LOCS_IN_REGION(she, dest))
copycelltosheet(src, she, dest, ALTER_LABEL);
if (scan_labels) cachelabels(she);
forceupdate(she);
}
/* do_sort -- sort block */ /*{{{*/
static int do_sort(Sheet *sheet)
{
do_mark(sheet, GET_MARK_ALL);
/* build menus */ /*{{{*/
const char* menu1[] =
{ _("cC)olumn"), _("rR)ow"), _("dD)epth"), NULL };
const char* menu2[] =
{ _("sS)ort region"), _("aA)dd key"), NULL };
const char* menu3[] =
{ _("aA)scending"), _("dD)escending"), NULL };
/*}}}*/
int last = -1;
Direction in_dir = (Direction)-2; /* cause run time error */
int dir;
/* ask for sort direction */ /*{{{*/
zero:
dir = line_menu(_("Sort block:"), menu1, 0);
switch (dir)
{
case 0: in_dir = IN_X; break;
case 1: in_dir = IN_Y; break;
case 2: in_dir = IN_Z; break;
case -2: case -1: return dir;
default: assert(0);
}
last=0;
/*}}}*/
Sortkey sk[MAX_SORTKEYS];
unsigned int key = 0;
bool doit = false;
do
{
/* ask for positions */ /*{{{*/
one: if (in_dir==IN_X) sk[key].x=0; else /* ask for x position */ /*{{{*/
{
sk[key].x = 0;
do
{
int c = line_numedit(&(sk[key].x), _("X position of key vector:"));
if (c == -1) return -1;
else if (c == -2) switch (last)
{
case -1: return -2;
case 0: goto zero;
case 2: goto two;
case 3: goto three;
case 5: goto five;
}
} while (sk[key].x < 0);
last = 1;
}
/*}}}*/
two: if (in_dir==IN_Y) sk[key].y=0; else /* ask for y position */ /*{{{*/
{
sk[key].y = 0;
do
{
int c = line_numedit(&(sk[key].y), _("Y position of key vector:"));
if (c == -1) return -1;
else if (c == -2) switch (last)
{
case -1: return -2;
case 0: goto zero;
case 1: goto one;
case 3: goto three;
case 5: goto five;
default: assert(0);
}
} while (sk[key].y < 0);
last = 2;
}
/*}}}*/
three: if (in_dir==IN_Z) sk[key].z=0; else /* ask for z position */ /*{{{*/
{
sk[key].z = 0;
do
{
int c = line_numedit(&(sk[key].z), _("Z position of key vector:"));
if (c == -1) return -1;
else if (c == -2) switch (last)
{
case -1: return -2;
case 0: goto zero;
case 1: goto one;
case 2: goto two;
case 5: goto five;
default: assert(0);
}
} while (sk[key].z < 0);
last = 3;
}
/*}}}*/
/*}}}*/
/* ask for sort key */ /*{{{*/
four: sk[key].sortkey=0;
int ckey = line_menu(_("Sort block:"), menu3, 0);
switch (ckey)
{
case 0: sk[key].sortkey |= ASCENDING; break;
case 1: sk[key].sortkey &= ~ASCENDING; break;
case -1: return -1;
case -2: switch (last)
{
case -1: return -2;
case 1: goto one;
case 2: goto two;
case 3: goto three;
default: assert(0);
}
default: assert(0);
}
last = 4;
/*}}}*/
++key;
five:
if (key == MAX_SORTKEYS) /* ask for sort comfirmation */ /*{{{*/
{
int c = line_ok(_("Sort block:"),0);
if (c == -1) return -1;
else if (c == -2) goto four;
else if (c == 0) doit = true;
}
/*}}}*/
else /* ask for sort or adding another key */ /*{{{*/
switch (line_menu(_("Sort block:"), menu2, 0))
{
case 0: doit = true; break;
case 1: doit = false; break;
case -1: return -1;
case -2: goto four;
default: assert(0);
}
/*}}}*/
last = 5;
} while (!doit);
const char *msg =
sortblock(sheet, sheet->mark1, sheet->mark2, in_dir, sk, key);
if (msg != NULL) line_msg(_("Sort block:"), msg);
return 0;
}
/*}}}*/
/* do_batchsort -- sort block in a batch*/ /*{{{*/
static void do_batchsort(Sheet *sheet, Direction dir, char* arg)
{
Sortkey sk[MAX_SORTKEYS];
int x1,y1,z1,x2,y2,z2;
unsigned int key = 0;
char* next;
while( *arg != '\0' )
{
while (isspace((int)*arg)) arg++;
sk[key].x=sk[key].y=sk[key].z=sk[key].sortkey=0;
switch (*arg)
{
case 'a': sk[key].sortkey|=ASCENDING; arg++; break;
case 'd': sk[key].sortkey&=~ASCENDING; arg++; break;
}
if ( *arg != '\0' && dir != IN_X ) { sk[key].x=strtol(arg, &next, 10); arg = next; }
if ( *arg != '\0' && dir != IN_Y ) { sk[key].y=strtol(arg, &next, 10); arg = next; }
if ( *arg != '\0' && dir != IN_Z ) { sk[key].z=strtol(arg, &next, 10); arg = next; }
key++;
}
do_mark(sheet, GET_MARK_ALL);
sortblock(sheet, sheet->mark1, sheet->mark2, dir, sk, key);
}
/*}}}*/
/* do_mirror -- mirror block */ /*{{{*/
static int do_mirror(Sheet *sheet)
{
/* variables */ /*{{{*/
int x1,y1,z1,x2,y2,z2,reply;
/*}}}*/
do_mark(sheet, GET_MARK_ALL);
/* ask for direction of mirroring */ /*{{{*/
{
const char *menu[] =
{ _("lL)eft-right"), _("uU)pside-down"), _("fF)ront-back"), NULL};
reply = line_menu(_("Mirror block:"), menu, 0);
if (reply < 0) return reply;
}
/*}}}*/
switch (reply)
{
/* 0 -- left-right */ /*{{{*/
case 0: mirrorblock(sheet, sheet->mark1, sheet->mark2, IN_X); break;
/*}}}*/
/* 1 -- upside-down */ /*{{{*/
case 1: mirrorblock(sheet, sheet->mark1, sheet->mark2, IN_Y); break;
/*}}}*/
/* 2 -- front-back */ /*{{{*/
case 2: mirrorblock(sheet, sheet->mark1, sheet->mark2, IN_Z); break;
/*}}}*/
default: assert(0);
}
return 0;
}
/*}}}*/
/* do_goto -- go to a specific cell */ /*{{{*/
static int do_goto(Sheet *sheet, const char *expr)
{
assert(sheet != (Sheet*)0);
size_t x = 0;
size_t offx = 0;
char buf[1024];
buf[0]='\0';
if (expr) strcpy(buf,expr);
else {
int c = line_edit(sheet, buf, sizeof(buf), _("Go to location:"), &x, &offx);
if (c < 0) return c;
}
char *s = buf;
Token **t = scan(&s);
if (t != EMPTY_TVEC)
{
Token value;
LOCATION_GETS(upd_l, sheet->cur);
upd_sheet = sheet;
value = eval_safe(t, FULL);
tvecfree(t);
if (value.type == LOCATION && IN_OCTANT(value.u.location))
movetoloc(sheet, value.u.location);
else
line_msg(_("Go to location:"), _("Not a valid location"));
tfree(&value);
}
return -1;
}
/*}}}*/
/* do_sheetcmd -- process one key press */ /*{{{*/
int do_sheetcmd(Sheet *cursheet, Key c, int moveonly)
{
switch ((int)c)
{
case K_GOTO: do_goto(cursheet, (const char *)0); break;
case K_COLWIDTH: do_columnwidth(cursheet); break;
case BLOCK_CLEAR: do_clear(cursheet); redraw_sheet(cursheet); break;
case BLOCK_INSERT: do_insert(cursheet); redraw_sheet(cursheet); break;
case BLOCK_DELETE: do_delete(cursheet); redraw_sheet(cursheet); break;
case BLOCK_MOVE: do_move(cursheet,0,0); redraw_sheet(cursheet); break;
case BLOCK_COPY: do_move(cursheet,1,0); redraw_sheet(cursheet); break;
case BLOCK_FILL: do_fill(cursheet); redraw_sheet(cursheet); break;
case FILL_BLOCK: fillwith(cursheet); redraw_sheet(cursheet); break;
case BLOCK_SORT: do_sort(cursheet); redraw_sheet(cursheet); break;
case BLOCK_MIRROR: do_mirror(cursheet); redraw_sheet(cursheet); break;
case ADJUST_LEFT:
case ADJUST_RIGHT:
case ADJUST_CENTER:
case ADJUST_DECIMAL:
case ADJUST_SCIENTIFIC:
case ADJUST_COMPACT:
case ADJUST_HEXACT:
case ADJUST_PRECISION:
case ADJUST_SHADOW:
case ADJUST_BOLD:
case ADJUST_UNDERLINE:
case ADJUST_TRANSPARENT:
case ADJUST_LABEL:
case ADJUST_LOCK:
case ADJUST_IGNORE: do_attribute(cursheet, c); break;
/* UP -- move up */ /*{{{*/
case K_UP:
{
relmoveto(cursheet, 0, -1, 0);
break;
}
/*}}}*/
/* DOWN -- move down */ /*{{{*/
case K_DOWN:
{
relmoveto(cursheet, 0, 1, 0);
break;
}
/*}}}*/
/* LEFT -- move left */ /*{{{*/
case K_LEFT:
{
relmoveto(cursheet, -1, 0, 0);
break;
}
/*}}}*/
/* RIGHT -- move right */ /*{{{*/
case K_RIGHT:
{
relmoveto(cursheet, 1, 0, 0);
break;
}
/*}}}*/
/* FIRSTL -- move to first line */ /*{{{*/
case K_FIRSTL:
case '<':
{
moveto(cursheet, -1, 0, -1);
break;
}
/*}}}*/
/* LASTL -- move to last line */ /*{{{*/
case K_LASTL:
case '>':
{
moveto(cursheet, -1, (cursheet->dimy ? cursheet->dimy-1 : 0), -1);
break;
}
/*}}}*/
/* HOME -- move to beginning of line */ /*{{{*/
case K_HOME:
{
moveto(cursheet, 0, -1, -1);
break;
}
/*}}}*/
/* END -- move to end of line */ /*{{{*/
case K_END:
{
moveto(cursheet, (cursheet->dimx ? cursheet->dimx-1 : 0), -1, -1);
break;
}
/*}}}*/
/* + -- move one sheet down */ /*{{{*/
case K_NSHEET:
case '+':
{
relmoveto(cursheet, 0, 0, 1);
break;
}
/*}}}*/
/* - -- move one sheet up */ /*{{{*/
case K_PSHEET:
case '-':
{
relmoveto(cursheet, 0, 0, -1);
break;
}
/*}}}*/
/* * -- move to bottom sheet */ /*{{{*/
case K_LSHEET:
case '*':
{
moveto(cursheet, -1, -1, (cursheet->dimz ? cursheet->dimz-1 : 0));
break;
}
/*}}}*/
/* _ -- move to top sheet */ /*{{{*/
case K_FSHEET:
case '_':
{
moveto(cursheet, -1, -1, 0);
break;
}
/*}}}*/
/* ENTER -- edit current cell */ /*{{{*/
case K_ENTER:
if (moveonly) break;
do_edit(cursheet, '\0', NULL, 0);
break;
/*}}}*/
/* MENTER -- edit current clocked cell */ /*{{{*/
case K_MENTER:
if (moveonly) break;
do_edit(cursheet, '\0', NULL, 1);
break;
/*}}}*/
/* ", @, digit -- edit current cell with character already in buffer */ /*{{{*/
case K_BACKSPACE:
case K_DC:
case '"':
case '@':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (moveonly) break;
do_edit(cursheet, c, NULL, 0);
break;
/*}}}*/
/* MARK -- toggle block marking */ /*{{{*/
case '.':
case K_MARK: if (moveonly) break; do_mark(cursheet, 0); break;
/*}}}*/
/* _("Save sheet file format:") -- save menu */ /*{{{*/
case K_SAVEMENU: if (moveonly) break; do_save(cursheet); break;
/*}}}*/
/* _("Load sheet file format:") -- load menu */ /*{{{*/
case K_LOAD:
case K_LOADMENU: if (moveonly) break; do_load(cursheet); break;
/*}}}*/
/* _("nN)ame") -- set name */ /*{{{*/
case K_NAME: if (moveonly) break; do_name(cursheet); break;
/*}}}*/
#ifdef ENABLE_HELP
case K_HELP: show_text(helpfile); break;
#else
case K_HELP: show_text(_("Sorry, manual is not installed.")); break;
#endif
case K_DUMPCELL: dump_current_cell(cursheet); break;
case K_ABOUT: show_text(_("<html><head><title>About teapot</title></head><body><center><pre>\n\n"
" ` ',` ' ' \n"
" ` ' ` ' ' \n"
" `' ' '`' \n"
" ' ` ' '` \n"
" ' '` ' ` '`` ` \n"
" `. Table Editor And Planner, or: \n"
" , . , , . . \n"
" ` ' ` ' ' \n"
" `::\\ /:::::::::::::::::\\ ___ \n"
" `::\\ /:::::::::::::::::::\\,'::::\\ \n"
" :::\\/:::::::::::::::::::::\\/ \\:\\ \n"
" :::::::::::::::::::::::::::\\ ::: \n"
" ::::::::::::::::::::::::::::; /:;' \n"
" `::::::::::::::::::::::::::::_/:;' \n"
" `::::::::::::::::::::::::::::' \n"
" `////////////////////////' \n"
" `:::::::::::::::::::::' \n"
"</pre>\n"
"<p>Teapot " VERSION "</p>\n"
"\n"
"<p>Original Version: Michael Haardt<br>\n"
"Current Maintainer: Joerg Walter<br>\n"
"Contibutions by: Glen Whitney<br>\b"
"Home Page: <a href='http://www.syntax-k.de/projekte/teapot/'>http://www.syntax-k.de/projekte/teapot/</a></p>\n"
"This distribution: <a href='https://code.studioinfinity.org/glen/teapot-spreadsheet/'>https://code.studioinfinity.org/glen/teapot-spreadsheet/</a></p>\n"
"\n"
"<p>Copyright 1995-2006 Michael Haardt,<br>\n"
"Copyright 2009-2010 Joerg Walter (<a href='mailto:info@syntax-k.de'>info@syntax-k.de</a>)<br>"
"Copyright 2019 Glen Whitney</p></center>\n"
"\f"
"<p>This program is free software: you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation, either version 3 of the License, or\n"
"(at your option) any later version.</p>\n"
"\n"
"<p>This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.</p>\n"
"\n"
"<p>You should have received a copy of the GNU General Public License\n"
"along with this program. If not, see <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.</p>"
"</body></html>"));
break;
/* MENU, / -- main menu */ /*{{{*/
case '/': if (!moveonly && do_sheetcmd(cursheet, show_menu(cursheet), 0)) return 1; break;
/*}}}*/
/* _("sS)ave") -- save in current native format */ /*{{{*/
case K_SAVE: do_save(cursheet); break;
/*}}}*/
/* _("cC)opy") -- copy block */ /*{{{*/
case K_COPY: if (moveonly) break; do_move(cursheet,1,1); break;
/*}}}*/
/* RECALC -- recalculate */ /*{{{*/
case K_RECALC: if (moveonly) break; forceupdate(cursheet); break;
/*}}}*/
/* _("Usage: clock(condition,location[,location])") -- clock */ /*{{{*/
case K_CLOCK:
{
Location w;
for (ALL_LOCS_IN_SHEET(cursheet, w))
clk(cursheet, w);
update(cursheet);
break;
}
/*}}}*/
/* NPAGE -- page down */ /*{{{*/
case K_NPAGE:
{
cursheet->offy+=(cursheet->maxy-3);
relmoveto(cursheet, 0, cursheet->maxy-3, 0);
break;
}
/*}}}*/
/* PPAGE -- page up */ /*{{{*/
case K_PPAGE:
{
cursheet->offy = (cursheet->offy>=(cursheet->maxy-3) ? cursheet->offy-(cursheet->maxy-3) : 0);
relmoveto(cursheet, 0, (cursheet->cur[Y] >= (cursheet->maxy-3) ? -(cursheet->maxy-3) : -cursheet->cur[Y]), 0);
break;
}
/*}}}*/
/* FPAGE -- page right */ /*{{{*/
case K_FPAGE:
{
cursheet->offx+=cursheet->width;
relmoveto(cursheet, cursheet->width, 0, 0);
break;
}
/*}}}*/
/* BPAGE -- page left */ /*{{{*/
case K_BPAGE:
{
cursheet->offx=(cursheet->offx>=cursheet->width ? cursheet->offx-cursheet->width : 0);
relmoveto(cursheet, (cursheet->cur[X]>=cursheet->width ? -cursheet->width : -cursheet->cur[X]), 0, 0);
break;
}
/*}}}*/
/* SAVEQUIT -- save and quit */ /*{{{*/
case K_SAVEQUIT:
{
if (moveonly) break;
if (do_save(cursheet)!=-2) return 1;
break;
}
/*}}}*/
/* _("qQ)uit") -- quit */ /*{{{*/
case K_QUIT: if (moveonly) break; return 1;
/*}}}*/
default:
if (isalpha(c) && !moveonly) do_edit(cursheet, c, NULL, 0);
break;
}
return 0;
}
/*}}}*/
/* main */ /*{{{*/
int main(int argc, char *argv[])
{
/* variables */ /*{{{*/
Sheet sheet,*cursheet;
int o;
const char *loadfile;
char ln[1024];
/*}}}*/
setlocale(LC_ALL, "");
find_helpfile(helpfile, sizeof(helpfile), argv[0]);
/* parse options */ /*{{{*/
while ((o=getopt(argc,argv,"bdhnrqHp:?"))!=EOF) switch (o)
{
/* b -- run batch */ /*{{{*/
case 'b': batch = true; break;
/*}}}*/
/* d -- increase debug level */ /*{{{*/
case 'd': ++debug_level; break;
/*}}}*/
/* n -- no quoted strings */ /*{{{*/
case 'n': quote = DIRECT_STRING; break;
/*}}}*/
/* q -- force quoted strings */ /*{{{*/
case 'q': quote = QUOTE_STRING; break;
/*}}}*/
/* H -- no row/column headers */ /*{{{*/
case 'H': header = false; break;
/*}}}*/
/* r -- always redraw */ /*{{{*/
case 'r':
{
always_redraw = true;
break;
}
/*}}}*/
/* p -- precision */ /*{{{*/
case 'p':
{
long n;
char *end;
n=strtol(optarg,&end,0);
if (*end || n < 0 || n > LDBL_DIG)
{
fprintf(stderr,
_("teapot: precision must be between 0 and %d.\n"), LDBL_DIG);
exit(1);
}
def_precision=n;
break;
}
/*}}}*/
/* default -- includes ? and h */ /*{{{*/
default:
{
fprintf(stderr,_(
"Usage: %s [-a] [-b] [-d]* [-h] [-n|-q] [-H] [-r] [-p digits] [file]\n"
" -a: use ASCII file format as default\n"
" -b: batch mode\n"
" -d: increase debug level, once for each occurrence\n"
" -h: print this message and exit\n"
" -n|-q: DO NOT or DO display strings in quotes, respectively\n"
" -H: hide row/column headers\n"
" -r: redraw more often\n"
" -p: set decimal precision\n"
), argv[0]);
exit(1);
}
/*}}}*/
}
loadfile = (optind<argc ? argv[optind] : (const char*)0);
/*}}}*/
cursheet = &sheet;
initialize_sheet(cursheet);
if (loadfile) /* load given sheet */ /*{{{*/
{
const char *msg;
cursheet->name=strdup(loadfile);
if (usexdr)
{
if ((msg=loadxdr(cursheet, cursheet->name)) != NULL)
line_msg(_("Load sheet from XDR file:"), msg);
}
else
if ((msg=loadport(cursheet, cursheet->name)) != NULL)
line_msg(_("Load sheet from ASCII file:"),msg);
}
/*}}}*/
if (batch) /* process batch */ /*{{{*/
while (fgets(ln,sizeof(ln),stdin)!=(char*)0)
{
/* variables */ /*{{{*/
size_t len;
char *cmd,*arg;
/*}}}*/
/* set cmd and arg */ /*{{{*/
++batchln;
len=strlen(ln);
if (len && ln[len-1]=='\n') ln[len-1]='\0';
cmd=ln; while (isspace((int)*cmd)) ++cmd;
arg=cmd;
while (*arg && !isspace((int)*arg)) ++arg;
while (isspace((int)*arg)) *arg++='\0';
/*}}}*/
/* goto location */ /*{{{*/
if (strcmp(cmd,"goto")==0) do_goto(cursheet,arg);
/*}}}*/
/* from location */ /*{{{*/
else if (strcmp(cmd,"from")==0)
{
do_goto(cursheet,arg);
do_mark(cursheet,1);
}
/*}}}*/
/* to location */ /*{{{*/
else if (strcmp(cmd,"to")==0)
{
do_goto(cursheet,arg);
do_mark(cursheet,2);
}
/*}}}*/
/* save-tbl file */ /*{{{*/
else if (strcmp(cmd,"save-tbl")==0) do_savetbl(cursheet,arg);
/*}}}*/
/* save-latex file */ /*{{{*/
else if (strcmp(cmd,"save-latex")==0) do_savelatex(cursheet,arg);
/*}}}*/
/* save-context file */ /*{{{*/
else if (strcmp(cmd,"save-context")==0) do_savecontext(cursheet,arg);
/*}}}*/
/* save-csv file */ /*{{{*/
else if (strcmp(cmd,"save-csv")==0) do_savecsv(cursheet,arg);
/*}}}*/
/* save-html file */ /*{{{*/
else if (strcmp(cmd,"save-html")==0) do_savehtml(cursheet,arg);
/*}}}*/
/* load-csv file */ /*{{{*/
else if (strcmp(cmd,"load-csv")==0) { loadcsv(cursheet,arg); forceupdate(cursheet); }
/*}}}*/
/* sort in x direction */ /*{{{*/
else if (strcmp(cmd,"sort-x")==0) do_batchsort(cursheet, IN_X, arg);
/*}}}*/
/* sort in y direction */ /*{{{*/
else if (strcmp(cmd,"sort-y")==0) do_batchsort(cursheet, IN_Y, arg);
/*}}}*/
/* sort in z direction */ /*{{{*/
else if (strcmp(cmd,"sort-z")==0) do_batchsort(cursheet, IN_Z, arg);
/*}}}*/
/* this is an unknown command */ /*{{{*/
else line_msg(_("Unknown batch command:"),cmd);
/*}}}*/
}
/*}}}*/
else /* process interactive input */ /*{{{*/
display_main(cursheet);
/*}}}*/
freesheet(cursheet,1);
fclose(stdin);
return 0;
}
/*}}}*/