AUSAM/source/ded/comm.c

#include "ded.h"
#include "match.h"
#include "char.h"

extern tdiag(); /* see do_rwrite */

struct COMMANDS
 { char *str;
    int (*func)();
    int params;
 };

extern char *get_range();
extern char one_letter();
extern
	do_quote(),
	do_ampersand(),
	do_slash(),
	do_semicolon(),
	do_percent(),
	do_unix(),
	do_at(),
	do_prime(),
	do_mark(),
	do_equals(),
	do_break(),
	do_copy(),
	do_copy(),
	do_linedelete(),
	do_dbug(),
	do_eof(),
	do_first(),
	do_file(),
	do_first(),
	do_last(),
	do_last(),
	do_move(),
	do_mark(),
	do_move(),
	do_exit(),
	do_printscreen(),
	do_quit(),
	do_rquit(),
	do_rwrite(),
	do_tab(),
	do_tof(),
	do_unbug(),
	do_write(),
	do_change();
/* 0 means no parameters,
 * 1 means parameters, leading spaces insignificant
 * 2 means parameters, leading spaces must be left alone
 */
struct COMMANDS  commands[]
 { "\"",       do_quote,       0,
    "&",        do_ampersand,   0,
    "/",        do_slash,       2,
    ";",        do_semicolon,   2,
    "%",        do_percent,     0,
    "!",        do_unix,        1,   /* see file unix.c */
    "@",       do_at,          0,
    "'",        do_prime,       1,
    ".",        do_mark,        1,
    "=",        do_equals,      1,
    "b",        do_break,       1,
    "c",        do_copy,        1,
    "copy",     do_copy,        1,
    "d",        do_linedelete,  1,
    "dbug",     do_dbug,        1,
    "eof",      do_eof,         0,
    "f",        do_first,       1,
    "file",     do_file,        0,
    "first",    do_first,       1,
    "l",        do_last,        1,
    "last",     do_last,        1,
    "m",        do_move,        1,
    "mark",     do_mark,        1,
    "move",     do_move,        1,
    "ok",       do_exit,        0,
    "p",        do_printscreen, 0,
    "q",        do_quit,        0,
    "reallyquit",
		do_rquit,     0,
    "reallywrite",
		do_rwrite,   0,
    "t",        do_tab,         1,
    "tof",      do_tof,         0,
    "unbug",    do_unbug,       1,
    "w",        do_write,       0,
    "x",        do_change,      1,
    0,
 };

do_command()
 { char line[ENOUGH];
    struct COMMANDS *com;
    char str[ENOUGH];
    register char *sp, *lp;
    register int lnum;

    /* find command string */
    diag_clear();
    copyrow(EDITROW,1,line);
    lp=line;
    sp = str;

    /* remember current position */
    store_c(&virt_c, (mode==INMODE ? &in_c: &edit_c));

    /* pick up command */
    while (*lp==' ') lp++;
    if (*lp==0)
      tdiag("?? no command");
    else
    if (alpha(*lp))
      while (alpha(*lp)) *sp++ = lcase(*lp++);
    else
    if (digit(*lp))
     { lnum = 0;
	do lnum = lnum*10+(*lp++ - '0'); while (digit(*lp));
	lnum--; /* I number from zero */
	if (*lp != 0) tdiag("!! command follows number !!");
	else
	if (lnum<0 || lnum>maxl) tdiag("!! no such line !!");
	else
	 { blobit(); disp_range(lnum, lnum, true); }
     }
    else
      /* if no alpha character, accept first character */
      *sp++ = *lp++;
    *sp=0;



    /* find procedure indexed by command and call it */
    com = commands;
    while (com->str != 0)
      if (streq(str,com->str))
	{  if (com->params || *lp==0)
	     { blobit();
		/* find first character of command */
		if (com->params==1) while(*lp==' ') lp++;
		(*(com->func))(lp);
		diag_clear();
		return(true); /* procedure will call tdiag or fdiag on error */
	     }
	    else fdiag("?? no parameters allowed ??");
	}
      else com++;

    tdiag("?? unrecognisable command");
 }

/* fill in missing argument from memory */
char *fillin(arg, old_arg, message)
char *arg, *old_arg, *message;
 { register char *a, *oa;
    register int i;

    if (arg==0 || *arg==0)
     { if (*old_arg==0) tdiag("?? %s ??", message);
	else
	 { diag_clear();
	    redraw(EDITROW, lastcol(EDITROW)+1, old_arg);
	    blobit();
	    return(old_arg);
	 }
     }
    else
     { a = arg; oa = old_arg;
	while (*oa++ = *a++);
	return(arg);
     }
  }

do_exit()
 { leave(mainloop,true); }

do_eof()
 { if (topl+LASTINROW<maxl) set_topl(maxl);
    else complain();
    i_mode(LASTINROW,lastcol(LASTINROW)+1);
 }

do_tof()
 { if (topl==0) complain();
    else set_topl(0);
    i_mode(0,0);
 }

do_linedelete()
 { del_row(in_c.row, (mode == INMODE ? &virt_c : &in_c));
    return(true);
 }

/* procedures to create ranges */
do_first(str)
char *str;
 { register char c, rc;

    c = one_letter(str); rc = c-'a';
    b_range[rc] = topl+in_c.row;
    if (e_range[rc] < b_range[rc]) e_range[rc] = b_range[rc];

    tell_range(c);
 }

do_last(str)
char *str;
 { register char c, rc;

    c = one_letter(str); rc = c-'a';
    e_range[rc] = topl+in_c.row;
    if (b_range[rc]<0 || e_range[rc] < b_range[rc]) b_range[rc] = e_range[rc];

    tell_range(c);
 }

do_mark(str)
char *str;
 { register char c, rc;

    c = one_letter(str); rc = c-'a';
    b_range[rc] = e_range[rc] = topl+in_c.row;

    tell_range(c);
 }

/* routines to move blocks of lines about */

do_move(str)
char *str;
 { shift_range(str, true); }

do_copy(str)
 { shift_range(str, false); }

shift_range(str, moving)
char *str;
int moving;
 { int begin, end, d1, d2, destination;
    int after;
    int size;

    str = get_range(str, &begin, &end);
    str = get_range(str, &d1, &d2);

    if (*str=='+') { after = true; str++; }
    else
    if (*str=='-') { after = false; str++; }
    else after = true; /* default */

    destination = (after ? d2+1 : d1);

    if (*str!=0) fdiag("!! only two arguments allowed !!");
    else
    if (begin>end) fdiag("!! invalid source range (%d to %d)", begin+1, end+1);
    else
    if ( (moving && begin<=destination && destination<=end) ||
	 (begin<destination && destination<=end) )
      fdiag("!! destination (%d) is within source (%d to %d) !!",
		    destination+1, begin+1, end+1);

    size = end-begin+1;

    savescreen(); saveedit();

    while (begin<=end)
      if (moving)
       { moveline(begin, destination);
	  if (destination>end)
	    /* begin stays the same (next line of source moves down one)
	     * end moves down one
	     * destination stays same (first line of destination moves down)
	     */
	    end--;
	  else
	    /* (destination is before begin)
	     * begin moves up one (next line of source moves down)
	     * destination moves up one
	     * end stays the same
	     */
	   { destination++; begin++; }
       }
      else
       { copyline(begin, destination);
	  if (destination>end)
	    /* begin moves up one, to next line of source
	     * end stays the same
	     * destination moves up one
	     */
	   { begin++; destination++; }
	  else
	    /* (destination is before begin)
	     * destination moves up one
	     * begin moves up two (destination has pushed it)
	     * end moves up one
	     */
	   { destination++; begin =+ 2; end++; }
       }

    /* if the line was moved after, display start of range! */
    topl = enclose_range(destination-size, destination-1, after);
    adj_maxl();
    refreshscreen();
    disp_range(destination-size, destination-1, after);
 }

/* get a single range argument from the command string */
char *get_range(str, a_first, a_second)
char *str;
int *a_first, *a_second;
 { register char c, rc;

    while (*str==' ') str++;
    if ((c = *str++)==0)
      fdiag("!! range argument missing !!");
    else
    if (c>='a' && c<='z') /* letter range */
     { rc = c-'a';
	if (b_range[rc]<0 || e_range[rc]>maxl || b_range[rc]>e_range[rc])
	  fdiag("!! no such range as %c !!", c);
	else
	 { *a_first = b_range[rc];
	    *a_second = e_range[rc];
	 }
     }
    else
    if (c=='.')
      *a_first = *a_second = topl+in_c.row;
    else
      fdiag("!! range must be letter or dot !!");

    /* eat the stuff after the argument */
    while (*str==' ') str++;
    if (*str==',') str++;
    return(str);
 }

/* display size of range */
tell_range(c)
char c;
 { register char rc;

    if (c=='.')
      tdiag("(current line is %d)", topl+in_c.row+1);
    else
    if (c<'a' || c>'z')
      editerror("invalid range character %o", c);
    else
     { rc = c-'a';
	if (b_range[rc]==e_range[rc])
	  tdiag("('%c' labels line %d)", c, b_range[rc]+1);
	else
	  tdiag("('%c' labels lines %d to %d)",
			c, b_range[rc]+1, e_range[rc]+1);
     }
 }

/* display a range on screen */
do_prime(str)
char *str;
 { int begin, end;
    register char c;
    int before;

    str = get_range(str, &begin, &end);

    if ((c = *str)=='-' || c==0) before = true;
    else
    if (c=='+') before = false;
    else
      fdiag("!! range must be single lower case letter !!");

    if (c!=0 && str[1]!=0 )
      fdiag("!! character follows %c !!",c);
    else
      disp_range(begin, end, before);
 }

/* give information about ranges */
do_equals(str)
char *str;
 { if (str[1]!=0)
      fdiag("!! range must be single character !!");
    else
      tell_range(*str=='.' ? *str : one_letter(str));
 }

/* read a single range character argument */
char one_letter(str)
char *str;
 { register char c;

    if ((c = *str) == 0 || c<'a' || c>'z' || str[1] != 0)
      tdiag("!! single lower-case letter required !!");

    return(c);
 }

/* routines to change to next or previous screenful */

do_quote()
 { if (topl+LASTINROW < maxl) set_topl(topl+NINROWS);
    else complain();
    i_mode(0,0);
 }

do_ampersand()
 { if (topl > 0) set_topl(topl-NINROWS);
    else complain();
    i_mode(0,0);
 }

do_quit()
 { savescreen();
    if (tmp_changed) tdiag("!! file has changed - use reallyquit to exit");
    else do_rquit();
 }

do_rquit()
 { leave(mainloop, false); }

char old_search[ENOUGH]; /* should contain zero first time */

do_slash(str)
char *str;
 { str = fillin(str, old_search, "no string specified");
    return(do_find(str, '/', FORWARD));
 }

do_semicolon(str)
char *str;
 { str = fillin(str, old_search, "no string specified");
    return(do_find(str, ';', BACKWARD));
  }

do_find(str, c, direction)
char *str;
char c;
int direction;
 { struct RE re[RESIZE];
    char *sp;

    if (*str==0) fdiag("?? no string specified ??");

    sp = build_re(re, str, c);
    if (*sp==0)
     { if ( find(re, direction, topl+in_c.row, in_c.col) )
	 { i_display(ms_line);
	    complain(); /* bleep */
	    i_mode(ms_line-topl, ms_col);
	 }
	else tdiag("!! can't find it !!");
     }
    else
     fdiag("!! string doesn't end with %c !!", c);
 }

char old_pat[ENOUGH]; /* I hope this contains zero to start with */

do_change(str)
char *str;
 { struct RE re[RESIZE];
    char *sp;
    char fin_ch;

    str = fillin(str, old_pat, "no pattern");
    if ((fin_ch = *str) != '/' && fin_ch != ';') fdiag("!! no pattern !!");

    sp = build_re(re,str+1,fin_ch);

    if (*sp==0) fdiag("!! no second string !!");

    return( change(re,sp,(fin_ch=='/' ? FORWARD : BACKWARD), fin_ch,
			topl+in_c.row,in_c.col) );
 }

do_dbug(str)
char *str;
 { if (*str==c_SPACE) str++;

    if (str[1] != '\0') fdiag("!! too many letters !!");
    else
    if (set_dbug(*str)) return(true);
    else fdiag("!! invalid debug option %c", *str);
 }

do_unbug(str)
char *str;
 { if (*str==c_SPACE) str++;

    if (str[1] != 0) fdiag("!! too many letters !!");
    else
    if (unset_dbug(*str)) return(true);
    else fdiag("!! invalid debug option %c", *str);
 }

do_percent()
 { int fline;

    fline = topl+in_c.row;
    set_topl(fline-NINROWS/2);
    i_mode(fline-topl,in_c.col);
 }

do_at()
 { return(do_unix(0)); } /* activates a newsh */

do_printscreen()
 { savescreen(); setupscreen(); return(true); }

do_tab(str)
char *str;
 { return(option(str,'t',&tbout)); }

do_break(str)
char *str;
 { return(option(str,'b',&txin)); }

do_write()
 { savescreen();
    if (!tmp_changed)
      tdiag("!! file hasn't changed - use reallywrite to force output");
    else
      do_rwrite();
 }

do_rwrite()
 { savefile(tdiag, true); complain();
    i_mode(in_c.row, in_c.col);
 }

option(str, com, var)
char *str, com;
int *var;
 { if (*str==0) fdiag("?? no option follows %c ??", com);
    else
    if (*str=='+' && str[1]==0) *var = true;
    else
    if (*str=='-' && str[1]==0) *var = false;
    else
      fdiag("?? unrecognisable option after %c ??", com);

    return(true);
 }

i_mode(srow,scol)
int srow,scol;
 { diag_clear();
    if (mode==INMODE)
     { position(srow,scol); leave(do_command,true); }
    else
     { set_c(srow,scol,&in_c); etoi(); }
 }

/* put a selected line on-screen */
i_display(fline)
int fline;
 { if (fline<topl+2 || topl+LASTINROW-2<fline)
      set_topl(fline-NINROWS/2);
 }

fdiag(str,c1,c2,c3,c4,c5)
char *str;
int c1,c2,c3,c4,c5;
 { diag(str,c1,c2,c3,c4,c5);
    complain();
    leave(do_command, false);
 }

tdiag(str,c1,c2,c3,c4,c5)
char *str;
int c1,c2,c3,c4,c5;
 { diag(str,c1,c2,c3,c4,c5);
    complain();
    leave(do_command, true);
 }

/* tell the user which file it is */
do_file()
 { tdiag(filename); }

/* show a range on the screen */
disp_range(begin, end, before)
int begin, end, before;
 { int newtop;

    newtop = enclose_range(begin, end, before);
    set_topl(newtop);
    i_mode((before ? begin-topl: end-topl), 0);
 }

int enclose_range(begin, end, before)
int begin, end, before;
 { register int size, res;

    if ((size = end-begin+1) <= NINROWS)
      res = (begin<topl+2 || end>topl+LASTINROW-2 ?
		begin-(NINROWS-size)/2 : topl);
    else
    if (before)
      res = (begin<topl+2 || begin>topl+NINROWS/2 ?
		begin-NINROWS/2 : topl);
    else
      res = (end<topl+2 || end>topl+LASTINROW-2 ?
		end-NINROWS/2 : topl);

    return(res<0 ? 0 : res+LASTINROW>=maxl ? maxl-(LASTINROW) : res);
 }