/*
 * Program:	Random routines
 *
 * Modifier:	Michael Seibel
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: mikes@cac.washington.edu
 *
 * Date:	19 Jan 1991
 * Last Edited:	6 Jan 1992
 *
 * Copyright 1991 by the University of Washington
 *
 *  Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */
/*
 * This file contains the command processing functions for a number of random
 * commands. There is no functional grouping here, for sure.
 */

#include        <stdio.h>
#include	"estruct.h"
#include	"pico.h"
#include        "edef.h"

int     tabsize;                        /* Tab size (0: use real tabs)  */

/*
 * Set fill column to n.
 */
setfillcol(f, n)
{
	char	numbuf[16];
	register int s;

	sprintf(numbuf,"%d",fillcol);
	s = mlreplyd("Set fill column to : ", numbuf, 4, QDEFLT);
	fillcol = atoi(numbuf);
        return(s);
}

/*
 * Display the current position of the cursor, in origin 1 X-Y coordinates,
 * the character that is under the cursor (in octal), and the fraction of the
 * text that is before the cursor. The displayed column is not the current
 * column, but the column that would be used on an infinite width display.
 * Normally this is bound to "C-X =".
 */
showcpos(f, n)
{
        register LINE   *clp;
        register long   nch;
        register int    cbo;
        register long   nbc;
        register int    cac;
        register int    ratio;
        register int    col;
        register int    lines;
        register int    thisline;
	char     buffer[80];

        clp = lforw(curbp->b_linep);            /* Grovel the data.     */
        cbo = 0;
        nch = 0;
        lines = 0;
        for (;;) {
                if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
		        thisline = lines;
                        nbc = nch;
                        if (cbo == llength(clp))
                                cac = '\n';
                        else
                                cac = lgetc(clp, cbo);
                }
                if (cbo == llength(clp)) {
                        if (clp == curbp->b_linep)
                                break;
                        clp = lforw(clp);
                        cbo = 0;
			lines++;
                } else
                        ++cbo;
                ++nch;
        }
	sprintf(buffer,"line %d of %d (%d%%), character %D of %D (%d%%)",
		thisline+1, lines+1, (100L*(thisline+1))/(lines+1),
		nbc, nch, (nch) ? (100L*nbc)/nch : 0);
	emlwrite(buffer);
        return (TRUE);
}

/*
 * Return current column.  Stop at first non-blank given TRUE argument.
 */
getccol(bflg)
int bflg;
{
        register int c, i, col;
        col = 0;
        for (i=0; i<curwp->w_doto; ++i) {
                c = lgetc(curwp->w_dotp, i);
                if (c!=' ' && c!='\t' && bflg)
                        break;
                if (c == '\t')
                        col |= 0x07;
                else if (c<0x20 || c==0x7F)
                        ++col;
                ++col;
        }
        return(col);
}



/*
 * Set tab size if given non-default argument (n <> 1).  Otherwise, insert a
 * tab into file.  If given argument, n, of zero, change to true tabs.
 * If n > 1, simulate tab stop every n-characters using spaces. This has to be
 * done in this slightly funny way because the tab (in ASCII) has been turned
 * into "C-I" (in 10 bit code) already. Bound to "C-I".
 */
tab(f, n)
{
        if (n < 0)
                return (FALSE);
        if (n == 0 || n > 1) {
                tabsize = n;
                return(TRUE);
        }
        if (! tabsize)
                return(linsert(1, '\t'));
        return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
}


/*
 * Insert a newline. Bound to "C-M". If we are in CMODE, do automatic
 * indentation as specified.
 */
newline(f, n)
{
	register int    s;

	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/
	if (n < 0)
		return (FALSE);

        if(optimize && (curwp->w_dotp != curwp->w_bufp->b_linep)){
	    int l;

	    if(worthit(&l)){
		if(curwp->w_doto != 0)
		  l++;
		scrolldown(curwp, l, n);
	    }
	}

	/* if we are in C mode and this is a default <NL> */
	/* pico's never in C mode */

	/* insert some lines */
	while (n--) {
		if ((s=lnewline()) != TRUE)
			return (s);
	}
	return (TRUE);
}



/*
 * Delete forward. This is real easy, because the basic delete routine does
 * all of the work. Watches for negative arguments, and does the right thing.
 * If any argument is present, it kills rather than deletes, to prevent loss
 * of text if typed with a big argument. Normally bound to "C-D".
 */
forwdel(f, n)
{
	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/
        if (n < 0)
                return (backdel(f, -n));

        if(optimize && (curwp->w_dotp != curwp->w_bufp->b_linep)){
	    int l;

	    if(worthit(&l) && curwp->w_doto == llength(curwp->w_dotp))
	      scrollup(curwp, l+1, 1);
	}

        if (f != FALSE) {                       /* Really a kill.       */
                if ((lastflag&CFKILL) == 0)
                        kdelete();
                thisflag |= CFKILL;
        }
        return (ldelete(n, f));
}



/*
 * Delete backwards. This is quite easy too, because it's all done with other
 * functions. Just move the cursor back, and delete forwards. Like delete
 * forward, this actually does a kill if presented with an argument. Bound to
 * both "RUBOUT" and "C-H".
 */
backdel(f, n)
{
        register int    s;

	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/
        if (n < 0)
                return (forwdel(f, -n));

	if(optimize && curwp->w_dotp != curwp->w_bufp->b_linep){
	    int l;

	    if(worthit(&l) && curwp->w_doto == 0 &&
	       lback(curwp->w_dotp) != curwp->w_bufp->b_linep){
		if(l == curwp->w_toprow)
		  scrollup(curwp, l+1, 1);
		else if(llength(lback(curwp->w_dotp)) == 0)
		  scrollup(curwp, l-1, 1);
		else
		  scrollup(curwp, l, 1);
	    }
	}

        if (f != FALSE) {                       /* Really a kill.       */
                if ((lastflag&CFKILL) == 0)
                        kdelete();
                thisflag |= CFKILL;
        }
        if ((s=backchar(f, n)) == TRUE)
                s = ldelete(n, f);
        return (s);
}



/*
 * killtext - delete the line that the cursor is currently in.
 *	      a greatly pared down version of its former self.
 */
killtext(f, n)
{
	register int	chunk;

        if (curbp->b_mode&MDVIEW)       /* don't allow this command if  */
                return(rdonly());       /* we are in read only mode     */

        if(optimize && (curwp->w_dotp != curwp->w_bufp->b_linep)){
	    int l;

	    if(worthit(&l))
	      scrollup(curwp, l, 1);
	}

        if ((lastflag&CFKILL) == 0)             /* Clear kill buffer if */
                kdelete();                      /* last wasn't a kill.  */
        thisflag |= CFKILL;
	curwp->w_doto = 0;		/* wack from the beginning of line */
	chunk = llength(curwp->w_dotp) + 1;	/* for the whole length. */
	return(ldelete(chunk, TRUE));
}


/*
 * Yank text back from the kill buffer. This is really easy. All of the work
 * is done by the standard insert routines. All you do is run the loop, and
 * check for errors. Bound to "C-Y".
 */
yank(f, n)
{
        register int    c;
        register int    i;
        extern   int    kused;

	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/
        if (n < 0)
                return (FALSE);

        if(optimize && (curwp->w_dotp != curwp->w_bufp->b_linep)){
	    int l;

	    if(worthit(&l) && !(lastflag&CFFILL)){
		register int  t = 0; 
		register int  i = 0;
		register int  ch;

		while((ch=kremove(i++)) >= 0)
		  if(ch == '\n')
		    t++;
		if(t+l < curwp->w_toprow+curwp->w_ntrows)
		  scrolldown(curwp, l, t);
	    }
	}

	if(lastflag&CFFILL)		/* if last command was fillpara() */
	  gotobop(FALSE, 1);		/* then go to the top of the para */
					/* then splat out the saved buffer */
        while (n--) {
                i = 0;
                while ((c=kremove(i)) >= 0) {
                        if (c == '\n') {
                                if (lnewline(FALSE, 1) == FALSE)
                                        return (FALSE);
                        } else {
                                if (linsert(1, c) == FALSE)
                                        return (FALSE);
                        }
                        ++i;
                }
        }

	if(lastflag&CFFILL){            /* if last command was fillpara() */
	    register LINE *botline;     /* blast the filled paragraph */
	    register LINE *topline;
	    register int  done = 0;

	    kdelete();
	    topline = curwp->w_dotp;
	    gotoeop(FALSE, 1);
	    botline = lforw(curwp->w_dotp);
	    curwp->w_dotp = topline;
	    if(topline != botline){
		while(!done){
		    if(lforw(curwp->w_dotp) == botline)
		      done++;
		    curwp->w_doto = 0;
		    ldelete((llength(curwp->w_dotp) + 1), FALSE);
		}
	    }
	    curwp->w_flag |= WFMODE;

	    if(Pmaster == NULL){
		sgarbk = TRUE;
		emlwrite("");
	    }
	}
        return (TRUE);
}
