/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/
#include        "list.h"

extern int tab_char;
char *sys_delim();

BUFFER	*
numberb(n)
int	n;
{
	register BUFFER	*bp;

	for (bp = bheadp; bp; bp = bp->b_bufp)
		if (bp->b_bufnum == n)
			return bp;
	return (BUFFER *) NULL;
}
int
killbuffer(n)
int	n;
{
	register BUFFER *bp;
	register BUFFER *bp1;
	register WINDOW *wp;
	extern	BUFFER	*numberb();

	/***********************************************/
	/*   Convert buffer ID to a buffer pointer.    */
	/***********************************************/
	if ((bp = numberb(n)) == NULL)
		return FALSE;

	/***********************************************/
	/*   Clear the text in the buffer.	       */
	/***********************************************/
	if (bclear(bp) != TRUE)
		return TRUE;
		
	/***********************************************/
	/*   Get rid of any pty's attached to buffer.  */
	/***********************************************/
	p_cleanup(bp);

	/***********************************************/
	/*   Free  up  the  association  with a local  */
	/*   keyboard.				       */
	/***********************************************/
	detach_local_keyboard(bp);
	
	/***********************************************/
	/*   Remove buffer from the buffer list.       */
	/***********************************************/
	if (bheadp == bp)
		bheadp = bp->b_bufp;
	else {
		for (bp1 = bheadp; bp1 && bp1->b_bufp != bp; )
			bp1 = bp1->b_bufp;
		if (bp1)
			bp1->b_bufp = bp->b_bufp;
		}
	/***********************************************/
	/*   If  the  buffer is being displayed, then  */
	/*   remove all references in windows.	       */
	/***********************************************/
	if (bp->b_nwnd != 0) {
		for (wp = wheadp; wp && bp->b_nwnd; wp = wp->w_wndp) {
			if (wp->w_bufp == bp) {
				--bp->b_nwnd;
				wp->w_bufp = bheadp;
				}
			}
		}
		
	/***********************************************/
	/*   Now  free  up the various bits of memory  */
	/*   allocated to the buffer.		       */
	/***********************************************/
	if (bp->b_title)
		chk_free(bp->b_title);
	if (bp->b_fname)
		chk_free((void *) bp->b_fname);
	ll_clear(bp->b_register);
	ll_free(bp->b_register);
	ll_free(bp->b_alist);
	delete_buffer_symbols(bp);
	free_line(bp->b_linep);

	chk_free((char *) bp);
	
	/***********************************************/
	/*   If  we  delete  the current buffer, then  */
	/*   set us to point to a random buffer.       */
	/***********************************************/
	if (curbp == bp)
		curbp = bheadp;
	return (TRUE);
}



/*
 * Look through the list of buffers, giving the user
 * a chance to save them.  Return TRUE if there are
 * any changed buffers afterwards. Buffers that don't
 * have an associated file don't count. Return FALSE
 * if there are no changed buffers.
 */
int
anycb() 
{
	register BUFFER *bp;
	int	nbuf = 0;
	char	buf[80];
	char	reply[4];
	extern BUFFER *scrap_bp;

	for (bp = bheadp; bp != NULL; bp = bp->b_bufp)
		if (*bp->b_fname && bp->b_nummod 
		   && (bp->b_flag & BF_SYSBUF) == 0 && bp != scrap_bp)
			nbuf++;

	if (nbuf == 0)
		return FALSE;
	(void) sprintf(buf, "%d buffer%s not been saved. Exit [ynw]? ",
		nbuf, nbuf == 1 ? " has" : "s have");

	reply[0] = NULL;
	ereply(buf, reply, 1);
	ewprintf("");
	if (reply[0] == 'Y' || reply[0] == 'y')
		return FALSE;
	if (reply[0] != 'W' && reply[0] != 'w')
		return TRUE;

	for (bp = bheadp; bp != NULL; bp = bp->b_bufp)
		if (*bp->b_fname && bp->b_nummod 
		   && (bp->b_flag & BF_SYSBUF) == 0 && bp != scrap_bp) {
		   	curbp = bp;
			argv[1].l_flags = F_NULL;
			argv[2].l_flags = F_NULL;
			argv[1].l_int = argv[2].l_int = 0;
			if (write_buffer() < 0)
				return TRUE;
			}
	return FALSE;
}
char *
backslash_to_fwd(str)
char *str;
{	register char *cp = str;
	for ( ; *cp; cp++)
		if (*cp == '\\')
			*cp = '/';
	return str;
}
# if defined(MONOCASE_FILENAMES)
void
lower_case(str)
char *str;
{	register char *cp;
	for (cp = str; *cp; cp++)
		if (isupper(*cp))
			*cp = tolower(*cp);
}
# endif
char	*
filename(fn)
char	*fn;
{	static char *buf = NULL;
	static int bufsiz = 0;
	register char	*cp;
	int	len;
	char	*curdir;
	
	
	curdir = get_cwd();
	len = strlen(fn) + strlen(curdir) + 3;
	/***********************************************/
	/*   Reallocate static buffer if necessary.    */
	/***********************************************/
	if (len > bufsiz) {
		if (buf)
			chk_free((void *) buf);
		buf = (char *) chk_alloc(len);
		bufsiz = len;
		}
	
# if	defined(MONOCASE_FILENAMES)
	lower_case(fn);
# endif
	backslash_to_fwd(fn);
	
	for (cp = fn; *cp; )
		if (*cp == '/' && cp[1] == '/')
			strcpy(cp, cp+1);
		else
			cp++;
# if	defined(VMS)
	{char	buf1[256];
	if (strchr(fn, '/') != NULL)
		fn = sys_fname_unix_to_vms(fn, buf1, sizeof buf1);
	if (strchr(fn, ':') != NULL)
		return fn;
	}
# endif
	if (fn[0] == '/')
		return fn;
# if defined(MSDOS)
	if (fn[1] == ':')
		return fn;
# endif
	if (curdir[0] == '/' && curdir[1] == NULL)
		sprintf(buf, "/%s", fn);
	else
		sprintf(buf, "%s%s%s", curdir, sys_delim(), fn);
# if	defined(VMS)
	{char *vms_filename_canon();
	return vms_filename_canon(buf);
	}
# else
	/*-----------------------------------------
	 *   Map dir/./file to dir/file, and
	 *   dir1/dir2/../file to dir1/file.
	 *-----------------------------------------*/
again:
	for (cp = buf; *cp; cp++) {
		if (*cp == '/' && cp[1] == '.' && cp[2] == '/') {
			strcpy(cp, cp+2);
			goto again;
			}
		if (*cp == '/' && cp[1] == '.' && cp[2] == '.' && cp[3] == '/') {
			char *cp1;
			if (cp == buf) {
				strcpy(buf, cp+3);
				goto again;
				}
			for (cp1 = cp - 1; cp1 >= buf; cp1--)
				if (*cp1 == '/')
					break;
			strcpy(cp1, cp+3);
			goto again;
			}
		}
	return buf;
# endif
}
void
inq_buffer()
{	BUFFER *bp;

	if (argv[1].l_flags == F_NULL) {
		acc_assign_int(curbp ? (long) curbp->b_bufnum : 0L);
		}
	else {
		bp = bfind(get_str(1), FALSE);
		acc_assign_int(bp ? (long) bp->b_bufnum : 0L);
		}
}
/*
 * Search for a buffer, by name.
 * If not found, and the "cflag" is TRUE,
 * create a buffer and put it in the list of
 * all buffers. Return pointer to the BUFFER
 * block for the buffer.
 */
BUFFER  *
bfind(buffer_name, cflag) 
register char *buffer_name; 
int	cflag;
{
	register BUFFER *bp;

	for (bp = bheadp; bp; bp = bp->b_bufp)
		if (strcmp(buffer_name, bp->b_fname) == 0)
			return bp;

	if (!cflag)
		return NULL;
	return buf_create(buffer_name);
}
/**********************************************************************/
/*   Following  function  creates a new buffer structure and puts it  */
/*   into the buffer list, in alphabetical order.		      */
/**********************************************************************/
BUFFER *
buf_create(name)
char	*name;
{	register BUFFER *bp;
	register LINE   *lp;
	u_int16	i;
	static	u_int16	buffer_number = 0;
	static	BUFFER	null_buffer = {0};

	if ((bp = (BUFFER *) chk_alloc(sizeof(BUFFER))) == NULL) {
		ewprintf("Can't create buffer");
		return NULL;
		}
	if ((lp=lalloc((RSIZE) 0)) == NULL) {
		chk_free((char *) bp);
		return NULL;
		}
	*bp = null_buffer;
	bp->b_fname = strdup(filename(name));
	bp->b_flag = tab_char ? BFTABS : 0;
	bp->b_alist = ll_init();
	bp->b_syms = spinit();
	bp->b_register = ll_init();
# ifdef S_IRUSR
	bp->b_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
# else
	bp->b_mode = 0644;
# endif
	bp->b_bufnum = ++buffer_number;
	bp->b_linep = lp;
	bp->b_numlines = bp->b_line = bp->b_col = 1;
	for (i = 0; i < NTABS; i++)
		bp->b_tabs[i] = i*8;	
	lp->l_fp = lp;
	lp->l_bp = lp;

	bp->b_bufp = bheadp;
	bheadp = bp;
	
	/***********************************************/
	/*   Keep buffer list in alphabetical order.   */
	/***********************************************/
	sort_buffer_list();

	return bp;
}
/**********************************************************************/
/*   This  function  sorts  the buffer list into alphabetical order.  */
/*   Called  when  we  create  a new buffer, and also when we change  */
/*   the buffer name.						      */
/**********************************************************************/
static int
buf_compare(b1, b2)
BUFFER **b1, **b2;
{	char	*f1 = (*b1)->b_fname;
	char	*f2 = (*b2)->b_fname;
	char	*cp;
	
	/***********************************************/
	/*   Sort  them  according  to  the  filename  */
	/*   component   --   ignore   the  directory  */
	/*   prefix.				       */
	/***********************************************/
	if ((cp = strrchr(f1, '/')) != NULL)
		f1 = cp + 1;
	if ((cp = strrchr(f2, '/')) != NULL)
		f2 = cp + 1;
	
	return strcmp(f1, f2);
}
void
sort_buffer_list()
{	int	num = 0;
	int	i;
	register BUFFER *bp;
	BUFFER	**array;

	/***********************************************/
	/*   First count how many buffers there are.   */
	/***********************************************/
	for (bp = bheadp; bp; bp = bp->b_bufp)
		num++;
		
	/***********************************************/
	/*   Now  allocate  an array so we can do the  */
	/*   sort.				       */
	/***********************************************/
	array = (BUFFER **) chk_alloc(sizeof(BUFFER *) * num);
	for (i = 0, bp = bheadp; bp; bp = bp->b_bufp)
		array[i++] = bp;
		
	/***********************************************/
	/*   Now sort the buffers.		       */
	/***********************************************/
	qsort(array, num, sizeof (BUFFER *), buf_compare);
	
	/***********************************************/
	/*   Now re-order the buffer list.	       */
	/***********************************************/
	bheadp = NULL;
	for (i = num - 1; i >= 0; i--) {
		array[i]->b_bufp = bheadp;
		bheadp = array[i];
		}
	chk_free((void *) array);
	
}
int
bclear(bp) 
register BUFFER *bp; 
{	char	**cpp;
	char	**next_cpp;
	
	bp->b_flag  &= ~(BFCHG|BFRO);
	while (bp->b_numlines > 1)
		lfree(bp, 1);

	bp->b_line = bp->b_col = 1;

	while (ll_pop(bp->b_alist))
		;
	
	/***********************************************/
	/*   Free  the  buffer allocated for the file  */
	/*   at readin time.			       */
	/***********************************************/
	for (cpp = bp->b_chunk; cpp; cpp = next_cpp) {
		next_cpp = (char **) *cpp;
		chk_free((void *) cpp);
		}
	bp->b_chunk = (char **) NULL;
	return TRUE;
}

/*
 * Display the given buffer in the given window. 
 */
void
showbuffer(bp, wp)
register BUFFER *bp; 
register WINDOW *wp; 
{
	register WINDOW *owp;

	/***********************************************/
	/*   If  we  dont  have a window, then forget  */
	/*   it.				       */
	/***********************************************/
	if (wp == NULL)
		return;
		
	if (wp->w_bufp == bp) {
		wp->w_flag |= WFHARD;
		return;
		}

	detach_buffer(wp);
	wp->w_bufp = bp;
	wp->w_old_line = 1;

	w_title(wp, bname(bp->b_fname), "");

	if (bp->b_nwnd++ == 0) {
		set_window_parms(wp, bp);
		return;
		}

	wp->w_flag |= WFHARD;
	/* already on screen, steal values from other window */
	for (owp = wheadp; owp; owp = owp->w_wndp)
		if (owp->w_bufp == bp && owp != wp) {
			wp->w_top_line = owp->w_top_line;
			wp->w_line  = owp->w_line;
			wp->w_col  = owp->w_col;
			wp->w_old_line = owp->w_old_line;
			return;
			}
			
	/***********************************************/
	/*   No  other  window  has buffer on view so  */
	/*   just  try  and  keep  the  current  line  */
	/*   somewhere in the middle of the window.    */
	/***********************************************/
	wp->w_col = 1;
	wp->w_line = bp->b_line;
	wp->w_top_line = wp->w_line - wp->w_h / 2;
	if (wp->w_top_line < 1)
		wp->w_top_line = 1;
}
void
set_window_parms(wp, bp)
register WINDOW *wp;
register BUFFER *bp;
{
	wp->w_flag |= WFHARD;
	wp->w_top_line = bp->b_top;
	wp->w_line = bp->b_line;
	wp->w_col  = bp->b_col;
	if (wp->w_line - wp->w_top_line >= wp->w_h)
		wp->w_top_line = wp->w_line;
}
void
set_buffer_parms(wp, bp)
register WINDOW *wp;
register BUFFER *bp;
{
	bp->b_line = wp->w_line;
	bp->b_col = wp->w_col;
	bp->b_top = wp->w_top_line;
}
void
inq_buffer_flags()
{
	BUFFER	*bp = argv[1].l_flags == F_INT ? 
		numberb(argv[1].l_int) : curbp;
	long	val = -1L;

	if (bp) {
		val = bp->b_flag;
		}
	acc_assign_int(val);
}
