/*      SC      A Spreadsheet Calculator
 *              Main driver
 *
 *              original by James Gosling, September 1982
 *              modifications by Mark Weiser and Bruce Israel,
 *                      University of Maryland
 *
 *              More mods Robert Bond, 12/86
 *              Major mods to run on VMS and AMIGA, 1/17/87
 *
 */

#include "sc.h"

extern char *malloc(),inch(),nmgetch(),get_qual();
extern int yyparse();
extern void move(),clrtoeol(),printw(),debug(),clearent(),free();
extern void clrtobot(),standout(),standend(),addstr(),editexp();
extern void endwin(),efree(),free(),EvalAll(),initscr(),dupcol();
extern void initkbd(),refresh(),touchwin(),clear(),help(),editv();
extern void edits(),insertrow(),insertcol(),deleterow(),deletecol();
extern void valueizerow(),valueizecol(),pullcells(),sync_refs();
extern void hiderow(),hidecol(),showrow_op(),showcol_op(),duprow();
#ifdef MSDOS
extern void printw1();
#endif

/* default column width */

#define DEFWIDTH 10
#define DEFPREC   2

#define RESCOL 4                /* columns reserved for row numbers */
#define RESROW 3                /* rows reserved for prompt, error, and column numbers */

struct ent *tbl[MAXROWS][MAXCOLS];

int strow, stcol;
int currow, curcol;
int savedrow, savedcol;
int FullUpdate;
int maxrow, maxcol;
int fwidth[MAXCOLS];
int precision[MAXCOLS];
char hidden_col[MAXCOLS];
char hidden_row[MAXROWS];
char line[1000];
int linelim;
int changed;
struct ent *to_fix;
struct enode *new();
struct ent *lookat();
struct enode *copye();
char *coltoa();

int modflg;

int showme = 1;                 /* 1 to display the current cell in the top line */
int batch = 0;

#ifdef ENGLISH
char *err[]={"At column A","The table can't be any wider",
"The table can't be any longer","At row zero",
"The table can't be any bigger"};
char deflt[]="default file is '%s'";
char curfile[1024] = "unnamed";
#endif
#ifdef GERMAN
char *err[]={"Bereits auf Spalte A","Die Tabelle ist auf max Breite",
"Die Tabelle ist auf max Hoehe","Bereits auf Zeile Null",
"Die Tabelle ist auf max Groesse"};
char deflt[]="Voreingestellt ist '%s'";
char curfile[1024] = "ohne_Namen";
#endif

void error (fmt, a, b, c, d, e)
     char *fmt;
{
  if (batch)
    fprintf (stderr, fmt, a, b, c, d, e);
  else
    {
      move (1, 0);
      clrtoeol ();
      printw (fmt, a, b, c, d, e);
    }
}

int seenerr;

void yyerror (err)
     char *err;
{
  if (seenerr)
    return;
  seenerr++;
  move (1, 0);
  clrtoeol ();
  printw ("%s: %.*s <= %s", err, linelim, line, line + linelim);
}

struct ent *
lookat (row, col)
{
  register struct ent **p;
  if (row < 0)
    row = 0;
  else if (row > MAXROWS - 1)
    row = MAXROWS - 1;
  if (col < 0)
    col = 0;
  else if (col > MAXCOLS - 1)
    col = MAXCOLS - 1;
  p = &tbl[row][col];
  if (*p == 0)
    {
      *p = (struct ent *) malloc (sizeof (struct ent));
      if (row > maxrow)
        maxrow = row;
      if (col > maxcol)
        maxcol = col;
      (*p)->label = 0;
      (*p)->flags = 0;
      (*p)->row = row;
      (*p)->col = col;
      (*p)->expr = 0;
      (*p)->v = (double) 0.0;
    }
  else if ((*p)->flags & is_deleted)
#ifdef ENGLISH
    debug ("But %s%d has been deleted!", coltoa (col), row);
#endif
#ifdef GERMAN
    debug ("Aber %s%d wurde geloescht!", coltoa (col), row);
#endif
  return *p;
}

/*
 * This structure is used to keep ent structs around before they
 * are deleted to allow the sync_refs routine a chance to fix the
 * variable references.
 * We also use it as a last-deleted buffer for the 'p' command.
 */

void free_ent (p)
     register struct ent *p;
{
  p->next = to_fix;
  to_fix = p;
  p->flags |= is_deleted;
}

void flush_saved ()
{
  register struct ent *p;
  register struct ent *q;

  if (!(p = to_fix))
    return;
  while (p)
    {
      clearent (p);
      q = p->next;
      free (p);
      p = q;
    }
  to_fix = 0;
}

void update ()
{
  register row, col;
  register struct ent **p;
  static lastmx, lastmy;
  static char *under_cursor = " ";
  int maxcol;
  int maxrow;
  int rows;
  int cols;
  register r;

  while (hidden_row[currow])    /* You can't hide the last row or col */
    currow++;
  while (hidden_col[curcol])
    curcol++;
  if (curcol < stcol)
    stcol = curcol, FullUpdate++;
  if (currow < strow)
    strow = currow, FullUpdate++;
  while (1)
    {
      register i;
      for (i = stcol, cols = 0, col = RESCOL;
           (col + fwidth[i]) < COLS - 1 && i < MAXCOLS; i++)
        {
          cols++;
          if (hidden_col[i])
            continue;
          col += fwidth[i];
        }
      if (curcol >= stcol + cols)
        stcol++, FullUpdate++;
      else
        break;
    }
  while (1)
    {
      register i;
      for (i = strow, rows = 0, row = RESROW;
           row < ROWS && i < MAXROWS; i++)
        {
          rows++;
          if (hidden_row[i])
            continue;
          row++;
        }
      if (currow >= strow + rows)
        strow++, FullUpdate++;
      else
        break;
    }
  maxcol = stcol + cols - 1;
  maxrow = strow + rows - 1;
  if (FullUpdate)
    {
      register int i;
      move (2, 0);
      clrtobot ();
      standout ();
      for (row = RESROW, i = strow; i <= maxrow; i++)
        {
          if (hidden_row[i])
            continue;
          move (row, 0);
          printw ("%-*d", RESCOL, i);
          row++;
        }
      move (2, 0);
      printw ("%*s", RESCOL, " ");
      for (col = RESCOL, i = stcol; i <= maxcol; i++)
        {
          if (hidden_col[i])
            continue;
          move (2, col);
          printw ("%*s", fwidth[i], coltoa (i));
          col += fwidth[i];
        }
      standend ();
    }
  for (row = strow, r = RESROW; row <= maxrow; row++)
    {
      register c = RESCOL;
      if (hidden_row[row])
        continue;
      for (p = &tbl[row][col = stcol]; col <= maxcol; col++, p++)
        {
          if (hidden_col[col])
            continue;
          if (*p && ((*p)->flags & is_changed || FullUpdate))
            {
              char *s;
              move (r, c);
              (*p)->flags &= ~is_changed;
              if ((*p)->flags & is_valid)
#ifndef MSDOS
                printw ("%*.*f", fwidth[col], precision[col], (*p)->v);
#else
                printw1 ("%*.*f", fwidth[col], precision[col], (*p)->v);
#endif
              if (s = (*p)->label)
                {
                  char field[1024];

                  strncpy (field, s, fwidth[col]);
                  field[fwidth[col]] = 0;
                  move (r, (*p)->flags & is_leftflush
                        ? c : c - strlen (field) + fwidth[col]);
                  addstr (field);
                }
            }
          c += fwidth[col];
        }
      r++;
    }

  move (lastmy, lastmx);
  if (inch () == '<') addstr (under_cursor);
  lastmy = RESROW;
  for (row = strow; row < currow; row++)
    if (!hidden_row[row]) lastmy++;
  lastmx = RESCOL;
  for (col = stcol; col <= curcol; col++)
    if (!hidden_col[col]) lastmx += fwidth[col];
  move (lastmy, lastmx);
  *under_cursor = inch ();
  addstr ("<");
  move (0, 0);
  clrtoeol ();
  if (linelim >= 0)
    {
      addstr (">> ");
      addstr (line);
    }
  else
    {
      if (showme)
        {
          register struct ent *p;
          p = tbl[currow][curcol];
          if (p && ((p->flags & is_valid) || p->label))
            {
              if (p->expr || !p->label)
                {
                  linelim = 0;
                  editexp (currow, curcol);
                }
              else
                {
                  sprintf (line, "%s", p->label);
                }
              addstr ("[");
              addstr (line);
              addstr ("]");
              linelim = -1;
            }
          else
            {
              addstr ("[]");
            }
        }
      move (lastmy, lastmx);
    }
  FullUpdate = 0;
}

void
quit ()
{
  endwin ();
  exit ();
}

void erasedb()
{
  register r, c;
  for (c = 0; c <= maxcol; c++)
    {
      fwidth[c] = DEFWIDTH;
      precision[c] = DEFPREC;
    }

  for (r = 0; r <= maxrow; r++)
    {
      register struct ent **p = &tbl[r][0];
      for (c = 0; c++ <= maxcol; p++)
        if (*p)
          {
            if ((*p)->expr)
              efree ((*p)->expr);
            if ((*p)->label)
              free ((*p)->label);
            free (*p);
            *p = 0;
          }
    }
  maxrow = 0;
  maxcol = 0;
  FullUpdate++;
}

char save[1024];

void writefile (fname)
     char *fname;
{
  register FILE *f;
  register struct ent **p;
  register r, c;

  if (*fname == 0)
    fname = &curfile[0];

  strcpy (save, fname);

  f = fopen (fname, "w");
  if (f == 0)
    {
#ifdef ENGLISH
      error ("Can't create %s", fname);
#endif
#ifdef GERMAN
      error ("Kann %s nicht anlegen", fname);
#endif
      return;
    }
#ifdef ENGLISH
  fprintf (f, "# This data file was generated by the Spreadsheet ");
  fprintf (f, "Calculator.\n");
  fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
#endif
#ifdef GERMAN
  fprintf (f, "# Diese Datei wurde vom Tabellenkalkulationsprogramm ");
  fprintf (f, "VC erzeugt.\n");
  fprintf (f, "# Sie sollten nicht darin editieren.\n\n");
#endif

  for (c = 0; c < MAXCOLS; c++)
    if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC)
      fprintf (f, "format %s %d %d\n", coltoa (c), fwidth[c], precision[c]);
  for (r = 0; r <= maxrow; r++)
    {
      p = &tbl[r][0];
      for (c = 0; c <= maxcol; c++, p++)
        if (*p)
          {
            if ((*p)->label)
              fprintf (f, "%sstring %s%d = \"%s\"\n",
                       (*p)->flags & is_leftflush ? "left" : "right",
                       coltoa (c), r, (*p)->label);
            if ((*p)->flags & is_valid)
              {
                editv (r, c);
                fprintf (f, "%s\n", line);
              }
          }
    }
  fclose (f);
  strcpy (curfile, save);

  modflg = 0;
#ifdef ENGLISH
  error ("File '%s' written.", curfile);
#endif
#ifdef GERMAN
  error ("Datei '%s' gespeichert.", curfile);
#endif
}

int modcheck (endstr)
     char *endstr;
{
  if (modflg)
    {
      char ch, lin[100];

      move (0, 0);
      clrtoeol ();
#ifdef ENGLISH
      sprintf(lin, "File '%s' is modified, save%s? (abort with ^g)", curfile, endstr);
#endif
#ifdef GERMAN
      sprintf(lin, "'%s' ist veraendert, abspeichern%s? (abbruch mit ^g)", curfile, endstr);
#endif
      addstr(lin);
      refresh ();
      ch = nmgetch ();
#ifdef ENGLISH
      if (toupper (ch) == 'Y')
#endif
#ifdef GERMAN
      if (toupper (ch) == 'J')
#endif
        writefile (curfile);
      else if (ch == ctl ('g'))
        return (1);
    }
  return (0);
}

void readfile (fname, eraseflg)
     char *fname;
     int eraseflg;
{
  register FILE *f;

  if (*fname == 0)
    fname = &curfile[0];
  strcpy (save, fname);

#ifdef ENGLISH
  if (eraseflg && strcmp (fname, curfile) && modcheck (" first"))
#endif
#ifdef GERMAN
  if (eraseflg && strcmp (fname, curfile) && modcheck (" zuerst"))
#endif
    return;

  f = fopen (save, "r");
  if (f == 0)
    {
#ifdef ENGLISH
      error ("Can't read %s", save);
#endif
#ifdef GERMAN
      error ("Kann %s nicht lesen", save);
#endif
      return;
    }

  if (eraseflg)
    erasedb ();

  while (fgets (line, sizeof line, f))
    {
      linelim = 0;
      if (line[0] != '#')
        yyparse ();
    }
  fclose (f);
  linelim = -1;
  modflg++;
  if (eraseflg)
    {
      strcpy (curfile, save);
      modflg = 0;
    }
  EvalAll ();
}

void main (argc, argv)
     char **argv;
{
  int inloop = 1;
  register int c;
  int edistate = -1;
  int arg = 1;
  int narg;
  int nedistate = -1;
  int running = 1;
  int wash = 0;
  register i;

  for (i = 0; i < MAXCOLS; i++)
    {
      fwidth[i] = DEFWIDTH;
      precision[i] = DEFPREC;
    }
  linelim = -1;

  if (argc > 1 && (!strcmp (argv[1], "-b")))
    {
      argc--, argv++;
      batch = 1;
    }

  if (!batch)
    initscr ();
  initkbd ();
  if (argc > 1)
    {
      strcpy (curfile, argv[1]);
      readfile (argv[1], 0);
    }
  modflg = 0;
  if (batch)
    exit (0);
#ifdef ENGLISH
  error ("VC 2.2  Type '?' for help.");
#endif
#ifdef GERMAN
  error ("VC 2.2  Hilfe mit '?'.");
#endif
  FullUpdate++;
  while (inloop)
    {
      running = 1;
      while (running)
        {
          nedistate = -1;
          narg = 1;
          if (edistate < 0 && linelim < 0 && (changed || FullUpdate))
            EvalAll (), changed = 0;
          update ();
          refresh ();
          move (1, 0);
          clrtoeol ();
          fflush (stdout);
          seenerr = 0;
          c = nmgetch ();
          if (wash)
            {
              wash = 0;
              FullUpdate++;
              touchwin ();
              clear ();
            }
          if ((c < ' ') || (c == 0177))
            {
              switch (c)
                {
                case ctl ('z'):
                  quit ();
                  break;
                case ctl ('r'):
                  FullUpdate++;
                  update();
                  refresh();
                  touchwin ();
                  clear ();
                  break;
                default:
#ifdef ENGLISH
                  error ("No such command  (^%c)", c + 0100);
#endif
#ifdef GERMAN
                  error ("Falscher Befehl (^%c)", c + 0100);
#endif
                  break;
                case ctl ('b'):
                  while (--arg >= 0)
                    {
                      if (curcol)
                        curcol--;
                      else
                        error(err[0]);
                      while (hidden_col[curcol] && curcol)
                        curcol--;
                    }
                  break;
                case ctl ('c'):
                  running = 0;
                  break;
                case ctl ('f'):
                  while (--arg >= 0)
                    {
                      if (curcol < MAXCOLS - 1)
                        curcol++;
                      else
                        error(err[1]);
                      while (hidden_col[curcol] && (curcol < MAXCOLS - 1))
                        curcol++;
                    }
                  break;
                case ctl ('g'):
                case ctl ('['):
                  linelim = -1;
                  move (1, 0);
                  clrtoeol ();
                  break;
                case 0177:
                case ctl ('h'):
                  while (--arg >= 0)
                    if (linelim > 0)
                      line[--linelim] = 0;
                  break;
                case ctl ('l'):
                  FullUpdate++;
                  break;
                case ctl ('m'):
                  if (linelim < 0)
                    line[linelim = 0] = 0;
                  else
                    {
                      linelim = 0;
                      yyparse ();
                      linelim = -1;
                    }
                  break;
                case ctl ('n'):
                  while (--arg >= 0)
                    {
                      if (currow < MAXROWS - 1)
                        currow++;
                      else
                        error(err[2]);
                      while (hidden_row[currow] && (currow < MAXROWS - 1))
                        currow++;
                    }
                  break;
                case ctl ('p'):
                  while (--arg >= 0)
                    {
                      if (currow)
                        currow--;
                      else
                        error(err[3]);
                      while (hidden_row[currow] && currow)
                        currow--;
                    }
                  break;
                case ctl ('q'):
                  break;        /* ignore flow control */
                case ctl ('s'):
                  break;        /* ignore flow control */
                case ctl ('t'):
                  showme ^= 1;
                  break;
                case ctl ('u'):
                  narg = arg * 4;
                  nedistate = 1;
                  break;
                case ctl ('v'):/* insert variable name */
                  if (linelim > 0)
                    {
                      sprintf (line + linelim, "%s%d", coltoa (curcol), currow);
                      linelim = strlen (line);
                    }
                  break;
                case ctl ('e'):/* insert variable expression */
                  if (linelim > 0)
                    editexp (currow, curcol);
                  break;
                case ctl ('a'):/* insert variable value */
                  if (linelim > 0)
                    {
                      struct ent *p = tbl[currow][curcol];

                      if (p && p->flags & is_valid)
                        {
                          sprintf (line + linelim, "%.*f",
                                   precision[curcol], p->v);
                          linelim = strlen (line);
                        }
                    }
                  break;
                }
            }
          else if ('0' <= c && c <= '9' && (linelim < 0 || edistate >= 0))
            {
              if (edistate != 0)
                {
                  if (c == '0') /* just a '0' goes to left col */
                    curcol = 0;
                  else
                    {
                      nedistate = 0;
                      narg = c - '0';
                    }
                }
              else
                {
                  nedistate = 0;
                  narg = arg * 10 + (c - '0');
                }
            }
          else if (linelim >= 0)
            {
              line[linelim++] = c;
              line[linelim] = 0;
            }
          else
            switch (c)
              {
              case '.':
                nedistate = 1;
                break;
              case ':':
                break;          /* Be nice to vi users */
              case '=':
#ifdef ENGLISH
                sprintf(line, "let %s%d = ", coltoa (curcol), currow);
#endif
#ifdef GERMAN
                sprintf(line, "Wert %s%d = ", coltoa (curcol), currow);
#endif
                linelim = strlen(line);
                break;
              case '/':
#ifdef ENGLISH
                sprintf (line, "copy [to] %s%d [from] ",
                         coltoa (curcol), currow);
#endif
#ifdef GERMAN
                sprintf (line, "Kopiere [nach] %s%d [von] ",
                         coltoa (curcol), currow);
#endif
                linelim = strlen (line);
                break;
              case '$':
                curcol = MAXCOLS - 1;
                while (!tbl[currow][curcol] && curcol > 0)
                  curcol--;
                break;
              case '?':
                wash++;
                help ();
                break;
              case '"':
#ifdef ENGLISH
                sprintf (line, "label %s%d = \"",
                         coltoa (curcol), currow);
#endif
#ifdef GERMAN
                sprintf (line, "Zeichenkette %s%d = \"",
                         coltoa (curcol), currow);
#endif
                linelim = strlen (line);
                break;
              case '<':
#ifdef ENGLISH
                sprintf (line, "leftstring %s%d = \"",
                         coltoa (curcol), currow);
#endif
#ifdef GERMAN
                sprintf (line, "Linke [Zeichenkette] %s%d = \"",
                         coltoa (curcol), currow);
#endif
                linelim = strlen (line);
                break;
              case '>':
#ifdef ENGLISH
                sprintf (line, "rightstring %s%d = \"",
                         coltoa (curcol), currow);
#endif
#ifdef GERMAN
                sprintf (line, "Rechte [Zeichenkette] %s%d = \"",
                         coltoa (curcol), currow);
#endif
                linelim = strlen (line);
                break;
              case 'e':
                editv (currow, curcol);
                break;
              case 'E':
                edits (currow, curcol);
                break;
              case 'f':
#ifdef ENGLISH
                sprintf (line, "format [for column] %s [is] ",
                         coltoa (curcol));
                error ("Current format is %d %d",
                       fwidth[curcol], precision[curcol]);
#endif
#ifdef GERMAN
                sprintf (line, "Format [der Spalte] %s [ist] ",
                         coltoa (curcol));
                error ("Jetziges Format ist %d %d",
                       fwidth[curcol], precision[curcol]);
#endif
                linelim = strlen (line);
                break;
              case 'P':
#ifdef ENGLISH
                sprintf (line, "put [database into] \"");
#endif
#ifdef GERMAN
                sprintf (line, "Schreibe [Tabelle nach] \"");
#endif
                if (*curfile)
                  error (deflt, curfile);
                linelim = strlen (line);
                break;
              case 'M':
#ifdef ENGLISH
                sprintf (line, "merge [database from] \"");
#endif
#ifdef GERMAN
                sprintf (line, "Hinzufuegen [Tabelle aus] \"");
#endif
                linelim = strlen (line);
                break;
              case 'G':
#ifdef ENGLISH
                sprintf (line, "get [database from] \"");
#endif
#ifdef GERMAN
                sprintf (line, "Lade [Tabelle von] \"");
#endif
                if (*curfile)
                  error (deflt, curfile);
                linelim = strlen (line);
                break;
              case 'W':
#ifdef ENGLISH
                sprintf (line, "write [listing to] \"");
#endif
#ifdef GERMAN
                sprintf (line, "Listing [nach] \"");
#endif
                linelim = strlen (line);
                break;
              case 'T': /* tbl output */
#ifdef ENGLISH
                sprintf (line, "tbl [listing to] \"");
#endif
#ifdef GERMAN
                sprintf (line, "tbl [Listing nach] \"");
#endif
                linelim = strlen (line);
                break;
              case 'i':
                switch (get_qual ())
                  {
                  case 'r':
                    insertrow (arg);
                    break;
                  case 'c':
                    insertcol (arg);
                    break;
                  default:
                    break;
                  }
                break;
              case 'd':
                switch (get_qual ())
                  {
                  case 'r':
                    deleterow (arg);
                    break;
                  case 'c':
                    deletecol (arg);
                    break;
                  default:
                    break;
                  }
                break;
              case 'v':
                switch (get_qual ())
                  {
                  case 'r':
                    valueizerow (arg);
                    break;
                  case 'c':
                    valueizecol (arg);
                    break;
                  default:
                    break;
                  }
                break;
              case 'p':
                {
                  register qual;

                  qual = get_qual ();
                  while (arg--)
                    pullcells (qual);
                  break;
                }
              case 'x':
                {
                  register struct ent **p;
                  register int c;

                  flush_saved ();
                  for (c = curcol; arg-- && c < MAXCOLS; c++)
                    {
                      p = &tbl[currow][c];
                      if (*p)
                        {
                          free_ent (*p);
                          *p = 0;
                        }
                    }
                  sync_refs ();
                  FullUpdate++;
                }
                break;
              case 'Q':
              case 'q':
                running = 0;
                break;
              case 'h':
                while (--arg >= 0)
                  {
                    if (curcol)
                      curcol--;
                    else
                      error(err[0]);
                    while (hidden_col[curcol] && curcol)
                      curcol--;
                  }
                break;
              case 'j':
                while (--arg >= 0)
                  {
                    if (currow < MAXROWS - 1)
                      currow++;
                    else
                      error(err[2]);
                    while (hidden_row[currow] && (currow < MAXROWS - 1))
                      currow++;
                  }
                break;
              case 'k':
                while (--arg >= 0)
                  {
                    if (currow)
                      currow--;
                    else
                      error(err[3]);
                    while (hidden_row[currow] && currow)
                      currow--;
                  }
                break;
              case 'l':
                while (--arg >= 0)
                  {
                    if (curcol < MAXCOLS - 1)
                      curcol++;
                    else
                      error(err[1]);
                    while (hidden_col[curcol] && (curcol < MAXCOLS - 1))
                      curcol++;
                  }
                break;
              case 'm':
                savedrow = currow;
                savedcol = curcol;
                break;
              case 'c':
                {
                  register struct ent *p = tbl[savedrow][savedcol];
                  register c;
                  register struct ent *n;

                  if (!p)
                    break;
                  FullUpdate++;
                  modflg = 1;
                  for (c = curcol; arg-- && c < MAXCOLS; c++)
                    {
                      n = lookat (currow, c);
                      clearent (n);
                      n->flags = p->flags;
                      n->v = p->v;
                      n->expr = copye (p->expr,
                                       currow - savedrow,
                                       c - savedcol);
                      n->label = 0;
                      if (p->label)
                        {
                          n->label = (char *)
                            malloc (strlen (p->label) + 1);
                          strcpy (n->label, p->label);
                        }
                    }
                  break;
                }
              case 'z':
                switch (get_qual ())
                  {
                  case 'r':
                    hiderow (arg);
                    break;
                  case 'c':
                    hidecol (arg);
                    break;
                  default:
                    break;
                  }
                break;
              case 's':
                switch (get_qual ())
                  {
                  case 'r':
                    showrow_op ();
                    break;
                  case 'c':
                    showcol_op ();
                    break;
                  default:
                    break;
                  }
                break;
              case 'a':
                switch (get_qual ())
                  {
                  case 'r':
                    while (arg--)
                      duprow ();
                    break;
                  case 'c':
                    while (arg--)
                      dupcol ();
                    break;
                  default:
                    break;
                  }
                break;
              default:
                if ((c & 0177) != c)
#ifdef ENGLISH
                  error ("Weird character, decimal '%d'.\n", (int) c);
#endif
#ifdef GERMAN
                  error ("Unbekanntes Zeichen, dezimal '%d'.\n", (int) c);
#endif
                else
#ifdef ENGLISH
                  error ("No such command  (%c)", c);
#endif
#ifdef GERMAN
                  error ("Befehl unbekannt  (%c)", c);
#endif
                break;
              }
          edistate = nedistate;
          arg = narg;
        }                       /* while (running) */
#ifdef ENGLISCH
      inloop = modcheck (" before exiting");
#endif
#ifdef GERMAN
      inloop = modcheck (" vor Beenden");
#endif
    }                           /*  while (inloop) */
  endwin ();
}
