/* -*- mode: C; mode: fold; -*- */
/* Copyright (c) 1992, 1998, 2000, 2002 John E. Davis
 * This file is part of JED editor library source.
 *
 * You may distribute this file under the terms the GNU General Public
 * License.  See the file COPYING for more information.
 */

#include "config.h"
#include "jed-feat.h"
/*{{{ Include Files */

#include <stdio.h>
#include <setjmp.h>
#if defined (__MSDOS__) || defined (__os2_16__)
# include <process.h>
# ifndef __MINGW32__
#  include <dos.h>
# endif
#endif
#include <limits.h>
#include <string.h>

#include "buffer.h"
#include "window.h"
#include "file.h"
#include "ins.h"
#include "misc.h"
#include "paste.h"
#include "sysdep.h"
#include "cmds.h"
#include "indent.h"

#if JED_HAS_SUBPROCESSES
#include "jprocess.h"
#endif


/*}}}*/

typedef struct /*{{{*/
{
   jmp_buf b;
}

/*}}}*/
jmp_buf_struct;

extern jmp_buf_struct Jump_Buffer, *Jump_Buffer_Ptr;

typedef struct Buffer_List_Type /*{{{*/
{
   Buffer *buf;
   struct Buffer_List_Type *next;
}

/*}}}*/
Buffer_List_Type;
Buffer_List_Type *Buffer_Stack;   /* a filo */

#ifndef ULONG_MAX
#define MAX_LONG (0xFFFFFFFFL | (0xFFFFFFFFL << (sizeof(long) / 8)))
#else
#define MAX_LONG ULONG_MAX
#endif

Buffer *CBuf;
Line *CLine;
Buffer_Local_Type Buffer_Local = /*{{{*/
{
   8
};

/*}}}*/

int Number_One = 1;		       /* these should be const but  */
int Number_Zero = 0;		       /* some compilers complain */
int Number_Two = 2;
int Number_Ten = 10;
char *Read_Only_Error = (char *) "Buffer is read only!";

unsigned int LineNum = 1;
unsigned int Max_LineNum;
int Point = 0;

/* move point to top of buffer */
int bob() /*{{{*/
{
   CLine = CBuf->beg;
   Point = 0;
   LineNum = 1;
   return(1);
}

/*}}}*/

int eob()                      /* point to end of buffer */ /*{{{*/
{
   CLine = CBuf->end;
   LineNum = Max_LineNum;
   if (CLine == NULL)
     {
	Point = 0;
	return(0);
     }
   
   Point = CLine->len - 1;
   if (Point < 0)
     {
	Point = 0;
	return(1);
     }
   
   if ((CLine->data[Point] != '\n') || (CBuf == MiniBuffer)) Point++;
   return(1);
}

/*}}}*/

int bol() /*{{{*/
{
   Point = 0;
   Goal_Column = 1;
   return(1);
}

/*}}}*/

int eol (void) /*{{{*/
{
   int nl = 1;
   if (CLine != NULL)
     {
	Point = CLine->len - 1;
	if (Point < 0)
	  {
	     Point = 0;
	     return 0;
	  }
	
	if (((CLine->data)[Point] != '\n') || (CBuf == MiniBuffer)) nl = 0;
	if (!nl) Point++;
     }
   
   else Point = 0;
   return 1;
}

/*}}}*/

int bobp()                /* returns 1 if top line of buffer, 0 otherwise */ /*{{{*/
{
   if ((CBuf->beg == CLine) && (Point == 0)) return(1); else return(0);
}

/*}}}*/

int eolp() /*{{{*/
{
   int len;
   
   if (CLine == NULL) return(1);
   len = CLine->len;
   if (len == 0) return(1);
   if (Point < len - 1) return(0);
   
   if (CBuf == MiniBuffer)
     {
	if (Point < len) return 0; else return (1);
     }
   
   
   /* Point is either len or len - 1 */
   
   if ((CLine->data)[len-1] == '\n') return (1);
   
   if (Point == len) return(1);
   Point = len - 1;
   return(0);
}

/*}}}*/

int eobp() /*{{{*/
{
   if (CLine != CBuf->end) return(0);
   return(eolp());
}

/*}}}*/

int bolp() /*{{{*/
{
   if (Point) return(0); else return(1);
}

/*}}}*/

/*  Attempt to goback n lines, return actual number. */
int prevline(int *n) /*{{{*/
{
   int i = 0;
   Line *prev;
   
   Point = 0;     /* return to beginning of this line */
   while (i < *n)
     {
	prev = CLine->prev;
	while((prev != NULL) && (prev->len == 0)) prev = prev->prev;
	if (prev == NULL) break;
	i++;
	CLine = prev;
     }
   
   if (i) eol();
   LineNum -= (unsigned int) i;
   return(i);
}

/*}}}*/

int nextline(int *np) /*{{{*/
{
   register Line *next;
   register int i = 0, n = *np;
   
   while(i < n)
     {
	next = CLine->next;
	if (next == NULL) break;
	CLine = next;
	i++;
     }
   
   if (i) Point = 0;
   LineNum += (unsigned int) i;
   return(i);
}

/*}}}*/

/* The algorithm:  go to Point = len - 1.  Then forward a line counted as 1 */
int forwchars(int *np) /*{{{*/
{
   int len, total = 0;
   unsigned char *p;
   Line *next;
   int n = *np;
   
   if (n < 0) return 0;
   if (CBuf == MiniBuffer)
     {
	total = CLine->len - Point;
	if (n > total) n = total;
	Point += n;
	return n;
     }
   
   while(1)
     {
	len = CLine->len;
	/* if (Point == len) return(total); */
	p = CLine->data + Point;
	while(n && (Point < len) && (*p != '\n'))
	  {
	     Point++;
	     n--;
	     p++;
	     total++;
	  }
	if (!n) return(total);
	if ((Point < len) && (*p == '\n'))
	  {
	     total++;
	     n--;
	  }
	
	if (NULL == (next = CLine->next))
	  {
	     if ((Point < len) && (*p == '\n')) total--;
	     return(total);
	  }
	
	Point = 0;
	CLine = next;
	LineNum++;
     }
}

/*}}}*/

int backwchars(int *np) /*{{{*/
{
   int total = 0;
   int n = *np;
   
   if (n < 0) return 0;
   if (CBuf == MiniBuffer)
     {
	if (n > Point) n = Point;
	Point -= n;
	return (n);
     }
   
   
   do
     {
	if (n <= Point)
	  {
	     total += n;
	     Point = Point - n;
	     return(total);
	  }
	n = n - Point - 1;   /* Point + newline */
	total += Point + 1;   /* ditto */
     }
   while(prevline(&Number_One));
   
   return(total - 1);
}

/*}}}*/

/* assuming 8 bit bytes */
#define BUNCH_SIZE 8 * sizeof(long)
static unsigned char NewLine_Buffer[1] = /*{{{*/
{
   '\n'
};

/*}}}*/

typedef struct Bunch_Lines_Type /*{{{*/
{
   struct Bunch_Lines_Type *next;
   unsigned long flags;			       /* describes which are free */
   Line lines[BUNCH_SIZE];
}

/*}}}*/
Bunch_Lines_Type;

static unsigned int Next_Free_Offset = BUNCH_SIZE;
static unsigned int Last_Free_Offset;
static Bunch_Lines_Type *Last_Free_Group;
static Bunch_Lines_Type *Bunch_Lines;
static unsigned int Number_Freed;      /* amount of Lines available */

static Line *create_line_from_bunch(void) /*{{{*/
{
   register Bunch_Lines_Type *b, *bsave;
   Line *l;
   unsigned long flags;
   int count;
   
   if (Last_Free_Group != NULL)
     {
	l = &Last_Free_Group->lines[Last_Free_Offset];
	flags = ((unsigned long) 1L << Last_Free_Offset);
	if ((Last_Free_Group->flags & flags) == 0)
	  {
	     exit_error("create group: internal error 1", 1);
	  }
	
	Last_Free_Group->flags &= ~flags;
	Last_Free_Group = NULL;
	Number_Freed--;
	return (l);
     }
   
   if (Next_Free_Offset < BUNCH_SIZE)
     {
	flags = ((unsigned long) 1L << Next_Free_Offset);
	if ((Bunch_Lines->flags & flags) == 0)
	  {
	     exit_error("free group: internal error 2", 1);
	  }
	
	
	Bunch_Lines->flags &= ~flags;
	Number_Freed--;
	return(&Bunch_Lines->lines[Next_Free_Offset++]);
     }
   
   /* search list */
   b = Bunch_Lines;
   if (b != NULL)
     {
	b = b->next;
     }
   
   if ((b != NULL) && Number_Freed)
     {
	bsave = b;
	do
	  {
	     if (b->flags)
	       {
		  flags = b->flags;
		  count = 0;
		  while ((flags & 1) == 0)
		    {
		       flags = flags >> 1;
		       count++;
		    }
		  l = &b->lines[count];
		  flags = (unsigned long) 1 << count;
		  if ((b->flags & flags) == 0)
		    {
		       exit_error("free group: internal error 2", 1);
		    }
		  b->flags &= ~flags;
		  Number_Freed--;
		  return (l);
	       }
	     b = b->next;
	  }
	while (b != bsave);
     }
   
   /* failed so now malloc new bunch */
   
   if (NULL == (b = (Bunch_Lines_Type *) SLmalloc (sizeof(Bunch_Lines_Type))))
     return NULL;
   
   if (Bunch_Lines == NULL)
     {
	Bunch_Lines = b;
	b->next = b;
     }
   else
     {
	b->next = Bunch_Lines->next;
	Bunch_Lines->next = b;
	Bunch_Lines = b;
     }
   
   b->flags = MAX_LONG;
   Next_Free_Offset = 1;
   b->flags &= ~(unsigned long) 1;
   Number_Freed += BUNCH_SIZE - 1;
   return(&b->lines[0]);
}

/*}}}*/


static void destroy_bunch_line(Line *l) /*{{{*/
{
#ifdef _MSC_VER
   Line *ll;
#else
   register Line *ll;
#endif
   
   register Bunch_Lines_Type *b, *bsave, *last, *next;
   static Bunch_Lines_Type *slast;
   
   if (slast != NULL) last = slast;
   else last = Bunch_Lines;
   
   b = bsave = last;
   
   do
     {
	ll = b->lines;
	if (
#if defined(SIXTEEN_BIT_SYSTEM)
	    /* stupid DOS and its memory segmentation forces me to consider
	     segment then offset */
	    (FP_SEG(ll) == FP_SEG(l)) &&
#endif
	    ((ll <= l) && (l < ll + BUNCH_SIZE)))
	  {
	     Last_Free_Offset = (unsigned int) (l - ll);

	     if (b->flags & (1L << Last_Free_Offset))
	       {
		  exit_error("free group: internal error 2", 1);
	       }
	     
	     b->flags |= (unsigned long) 1L << Last_Free_Offset;
	     
	     /* if this whole structure is free, free it */
	     if (b->flags == MAX_LONG)
	       {
		  if (last == b)
		    {
		       while ((next = last->next) != b) last = next;
		    }
		  
		  last->next = b->next;
		  
		  if (b == Bunch_Lines)
		    {
		       if (last == b)
			 {
			    last = NULL;
			    /*
			     last = last->next;
			     if (last == b) last = NULL; */
			 }
		       
		       Bunch_Lines = last;
		       Next_Free_Offset = BUNCH_SIZE;
		    }
		  
		  SLfree ((char *)b);
		  
		  if (last == b) last = NULL;
		  b = NULL;
		  Number_Freed -= BUNCH_SIZE - 1;
		  slast = last;
	       }
	     else
	       {
		  Number_Freed++;
	       }
	     
	     Last_Free_Group = b;
	     if (Bunch_Lines == NULL) goto L1;
	     return;
	  }
	last = b;
	b = b->next;
     }
   while (b != bsave);
   L1:
   exit_error("destroy_bunch_line: internal error 1", 1);
}

/*}}}*/

Line *make_line1(unsigned int size) /*{{{*/
{
   Line *new_line;
   unsigned char *data = NULL;
   unsigned int chunk;
   
   /* 4 byte chunks */
#if defined(SIXTEEN_BIT_SYSTEM)
   chunk = (size + 3) & 0xFFFCU;
#else
   chunk = ((size + 3)) & 0xFFFFFFFCU;
#endif
   new_line = (Line *) create_line_from_bunch();
   if (new_line != NULL)
     {
	if (size == 1)
	  {
	     data = NewLine_Buffer;
#ifdef KEEP_SPACE_INFO
	     chunk = 1;
#endif
	  }
	else data = (unsigned char *) SLmalloc (chunk);   /* was chunk + 1 */
     }
   
   if ((new_line == NULL) || (data == NULL))
     {
	*Error_Buffer = 0;	       /* this is critical */
	jed_verror ("Malloc Error in make_line: requested size: %d.", size);
	longjmp(Jump_Buffer_Ptr->b, 1);
	/* exit_error(buff); */
     }
   new_line->data = data;
   new_line->len = 0;
#ifdef KEEP_SPACE_INFO
   new_line->space = chunk;
#endif
#if JED_HAS_LINE_ATTRIBUTES
   new_line->flags = 0;
#endif
   return(new_line);
}

/*}}}*/

/* adds a new link to list of lines at current point */
unsigned char *make_line(unsigned int size) /*{{{*/
{
   Line *new_line;
   
   new_line = make_line1(size);
   /* if CLine is Null, then we are at the top of a NEW buffer.  Make this
    explicit. */
   if (CLine == NULL)
     {
	new_line -> prev = NULL;
	new_line -> next = NULL;
	CBuf -> beg = CBuf ->end = new_line;
     }
   else if (CLine == CBuf->end) /* at end of buffer */
     {
	CBuf->end  = new_line;
	new_line->next = NULL;
	new_line->prev = CLine;
	CLine->next = new_line;
     }
   else
     {
	new_line -> next = CLine -> next;
	if (CLine->next != NULL) CLine->next->prev = new_line;
	CLine->next = new_line;
	new_line->prev = CLine;
     }
   
   if (CLine == NULL)
     {
	Max_LineNum = LineNum = 1;
     }
   else
     {
	Max_LineNum++;
	LineNum++;
     }
   CLine = new_line;
   
   return(CLine->data);
}

/*}}}*/

void free_line(Line *line) /*{{{*/
{
   register unsigned char *dat = line->data;
   
   if (dat != NewLine_Buffer) SLfree ((char *)dat);
   destroy_bunch_line(line);
}

/*}}}*/

/* deletes the line we are on and returns the prev one.  It does not
 * delete the top line of the buffer.   Furthermore, it does not
 *  update any marks.  */

int delete_line() /*{{{*/
{
   Line *n, *p, *tthis;
   
   p = CLine -> prev;
   if (p == NULL) return(1);
   
   n = CLine -> next;
   tthis = CLine;
   if (n == NULL)
     {
	CBuf->end = p;
	p->next = NULL;
     }
   else
     {
	p->next = n;
	n->prev = p;
     }
   
   free_line(tthis);
   CLine = p;
   LineNum--;
   Max_LineNum--;
   
   return(0);
}

/*}}}*/

unsigned char *remake_line(unsigned int size) /*{{{*/
{
   unsigned char *d = CLine->data;
   unsigned int mask;
#if defined(SIXTEEN_BIT_SYSTEM)
   mask = 0xFFFCu;
#else
   mask = 0xFFFFFFFCu;
#endif
   size = (unsigned) (size + 3) & mask;   /* 4 byte chunks */
   
   if (d == NewLine_Buffer)
     {
	if (NULL != (d = (unsigned char *) SLmalloc (size))) *d = '\n';
     }
   else
     {
#if 1				       /* NOTE: This was #if 0.  Why??? */
#ifndef KEEP_SPACE_INFO
	if (((CLine->len + 3) & mask) == size) return d;
#endif
#endif
	d = (unsigned char *) SLrealloc ((char *) d, size);
     }
   
   if (d == NULL)
     {
	*Error_Buffer = 0;	       /* critical error */
	jed_verror ("remake_line: realloc error!, size = %d", size);
	longjmp(Jump_Buffer_Ptr->b, 1);
	/* exit_error(buf); */
     }
   
#ifdef KEEP_SPACE_INFO
   CLine->space = size;
#endif
   CLine->data = d;
   return(d);
}

/*}}}*/

void uniquely_name_buffer (Buffer *b, char *trry) /*{{{*/
{
   Buffer *bnext;
   int version = 0, n;
   char *neew;
   char *name;
   
   n = strlen(trry);
   neew = SLmalloc (n + 12);
   if (neew == NULL)
     exit_error ("Out of memory", 0);

   strcpy (neew, trry);

   while (1)
     {
	bnext = b->next;
	while (bnext != b)
	  {
	     if ((bnext->name != NULL)
		 && (0 == strcmp(neew, bnext->name)))
	       break;

	     bnext = bnext->next;
	  }
	
	if (bnext == b)
	  {
	     name = SLang_create_slstring (neew);
	     if (name == NULL)
	       exit_error ("Out of memory", 0);
	     SLang_free_slstring (b->name);
	     b->name = name;
	     break;
	  }

	version++;
	sprintf (neew + n, "<%d>", version);
     }
   SLfree (neew);
}

/*}}}*/

/* make a buffer and insert it in the list */
Buffer *make_buffer (char *name, char *dir, char *file) /*{{{*/
{
   Buffer *newB;
   
   newB = (Buffer *) jed_malloc0 (sizeof(Buffer));
   
   if (newB == NULL)
     exit_error("Out of memory", 0);

   newB->keymap = Global_Map;
   jed_set_buffer_ctime (newB);
   newB->local_vars.tab = Jed_Tab_Default;
   
   if (CBuf == NULL)
     {
	newB->next = newB;
	newB->prev = newB;
     }
   else
     {
	newB->next = CBuf;
	newB->prev = CBuf->prev;
	CBuf->prev->next = newB;
	CBuf->prev = newB;
     }
   
#ifdef IBMPC_SYSTEM
   newB->flags |= ADD_CR_ON_WRITE_FLAG;
#endif
   newB->syntax_table = Default_Syntax_Table;

   buffer_filename (newB, dir, file);
   if ((name != NULL)
       || (newB->name == NULL))
     {
	if (name == NULL) name = "*scratch*";
	uniquely_name_buffer (newB, name);
     }

   return (newB);
}

/*}}}*/

/* if there is a window attached to this buffer, then there are problems
 *  if we get to update() without attaching another one to it.  So
 *  beware! Always make sure CBuf is set too!   kill_buffer command
 *  takes care of this */
void delete_buffer(Buffer *buf) /*{{{*/
{
   Line *l,*n;
   Jed_Mark_Array_Type *m, *m1;


   jed_widen_whole_buffer (buf);

   jed_unlock_buffer_file (buf);

   if (buf -> beg != NULL) for (l = buf -> beg; l != NULL; l = n)
     {
	n = l -> next;
	free_line(l);
	/* SLfree( l->data); SLfree( l); */
     }

   m = buf->mark_array;
   while (m != NULL)
     {
	m1 = m->next;
	SLfree ((char *)m);
	m = m1;
     }
   
   m = buf->spot_array;
   while (m != NULL)
     {
	m1 = m->next;
	SLfree ((char *)m);
	m = m1;
     }

   if (buf->user_marks != NULL) free_user_marks (buf);
   
   if (buf->undo != NULL) delete_undo_ring (buf);
   
#if JED_HAS_BUFFER_LOCAL_VARS 
   jed_delete_blocal_vars (buf->blocal_table);
#endif
   
#if JED_HAS_SAVE_NARROW
   while (buf->save_narrow != NULL)
     jed_free_saved_narrow (buf);
#endif
   
#if JED_HAS_COLOR_COLUMNS
   SLfree ((char *)buf->column_colors);
#endif
   buf->prev->next = buf->next;
   buf->next->prev = buf->prev;

#if JED_HAS_TTY_MENUS
   jed_delete_menu_bar (buf->menubar);
#endif

#if JED_HAS_SUBPROCESSES
   if (buf->subprocess) jed_kill_process (buf->subprocess - 1);
#endif
   
   SLang_free_slstring (buf->dir);
   SLang_free_slstring (buf->file);
   SLfree (buf->dirfile);
   SLang_free_slstring (buf->name);
   SLang_free_slstring (buf->mode_string);

   SLfree ((char *)buf);
}

/*}}}*/

void goto_line (int *np) /*{{{*/
{
   unsigned int n;
   unsigned int half1 = LineNum / 2;
   unsigned int half2 = (Max_LineNum + LineNum) / 2;
   int sn;
   
   if (*np <= 1) n = 0; else n = (unsigned int) *np;
   
   if (n < LineNum)
     {
	if (n > half1)
	  {
	     sn = (int) (LineNum - n);
	     prevline(&sn);
	  }
	else
	  {
	     bob();
	     sn = (int) n;
	     if (sn-- > 0) nextline(&sn);
	  }
     }
   else if (n < half2)
     {
	sn = (int) (n - LineNum);
	nextline(&sn);
     }
   else
     {
	eob();
	sn = (int) (Max_LineNum - n);
	prevline(&sn);
     }
   Point = 0;
}

/*}}}*/

Line *dup_line(Line *l) /*{{{*/
{
   Line *neew;
   int n;
   unsigned char *p, *q;
   
#ifdef KEEP_SPACE_INFO
   n = l->space;
#else
   n = l->len;
#endif
   
   neew = (Line *) SLmalloc (sizeof(Line));
   if ((neew == NULL) ||
       (NULL == (neew->data = (unsigned char *) SLmalloc (n))))
     {
	exit_error("Out of memory", 0);
     }
   neew->next = l->next;
   neew->prev = l->prev;
   neew->len = n;
#ifdef KEEP_SPACE_INFO
   neew->space = l->space;
#endif
   p = neew->data;
   q = l->data;
   
   while (n--) *p++ = *q++;
   return(neew);
}

/*}}}*/

Buffer *find_buffer(char *name) /*{{{*/
{
   Buffer *b;
   
   b = CBuf;
   do
     {
	if (!strcmp(b->name, name)) return(b);
	b = b->next;
     }
   while(b != CBuf);
   
#if !JED_FILE_PRESERVE_CASE
   b = CBuf;
   do
     {
	if (!jed_case_strcmp (b->name, name)) return(b);
	b = b->next;
     }
   while(b != CBuf);
#endif
   return(NULL);
}

/*}}}*/

int buffer_exists(Buffer *b) /*{{{*/
{
   Buffer *c = CBuf;
   do
     {
	if (b == c) return 1;
	c = c->next;
     }
   while (c != CBuf);
   return 0;
}

/*}}}*/

int switch_to_buffer(Buffer *buf) /*{{{*/
{
   /*  save this buffer position */
   CBuf->line = CLine;
   CBuf->point = Point;
   CBuf->linenum = LineNum;
   CBuf->max_linenum = Max_LineNum;
   
   /* local variables */
   SLMEMCPY ((char *) &CBuf->local_vars, (char *) &Buffer_Local, sizeof (Buffer_Local_Type));
   
   if (buf == CBuf) return(0);
   
#if 0
   buf->prev->next = buf->next;
   buf->next->prev = buf->prev;
   
   buf->next = CBuf;
   buf->prev = CBuf->prev;
   CBuf->prev->next = buf;
   CBuf->prev = buf;
#endif
   
   /* now restore new buffer */
   CBuf = buf;
   CLine = CBuf->line;
   Point = CBuf->point;
   
   /* Buffer local variables */
   SLMEMCPY ((char *) &Buffer_Local, (char *) &CBuf->local_vars, sizeof (Buffer_Local_Type));
	   
   LineNum = CBuf->linenum;
   Max_LineNum = CBuf->max_linenum;
   
   if (CLine == NULL)
     {
	make_line(25);
	Point = 0;
     }
   CBuf->line = CLine;
   CBuf->point = Point;
   jed_set_umask (CBuf->umask);
   return(1);
}

/*}}}*/

#if 1
Buffer *find_file_buffer(char *file) /*{{{*/
{
   Buffer *b;

   file = jed_expand_link (file);
   if (file == NULL)
     return NULL;
   
   b = CBuf;
   do
     {
	if ((b->dirfile != NULL)
# if JED_FILE_PRESERVE_CASE
	    && (0 == strcmp(b->dirfile, file))
# else
	    && (0 == jed_case_strcmp (b->dirfile, file))
# endif
	    )
	  {
	     SLfree (file);
	     return b;
	  }
	b = b->next;
     }
   while (b != CBuf);
   SLfree (file);
   return NULL;
}

#else
/* we assume that file has already been expanded--- that is, canonical. */
Buffer *find_file_buffer(char *file) /*{{{*/
{
   Buffer *b;
   char *f;
   long n, m;
   char *dirfile;
#ifdef REAL_UNIX_SYSTEM
   int inode, device;
   
   get_inode_info (file, &device, &inode);
#endif

   f = extract_file(file);
   n = f - file;
   
   b = CBuf;
   do
     {
#ifdef REAL_UNIX_SYSTEM
	if ((device != -1) && (inode != -1)
	    && (b->device != -1) && (b->inode != -1))
	  {
	     if ((inode == b->inode) 
		 && (device == b->device)
		 && !strcmp(b->file, f)
		 )
	       return b;
	  }
	else 
	  {
	     m = strlen(b->dir);
	     if ((m == n) && (!strcmp(b->file,f))
		 && (!strncmp(b->dir, file, (unsigned int) n)))
	       return b;
	  }
#else
	m = strlen(b->dir);
	
	if ((m == n) 
# if JED_FILE_PRESERVE_CASE
	    && (0 == strcmp(b->file, f))
	    && (0 == strncmp(b->dir, file, (unsigned int) n))
# else
	    && (0 == jed_case_strcmp (b->file,f))
	    && (0 == jed_case_strncmp (b->dir, file, (unsigned int) n))
# endif
	    )
	  return b;
#endif
	b = b->next;
     }
   while (b != CBuf);
   
   return NULL;
}
/*}}}*/
#endif

/* take a dir and a filename, expand them then put in buffer structure */
void buffer_filename(Buffer *b, char *dir, char *file) /*{{{*/
{
   unsigned int dirlen;
   char *dirfile;

   if (dir == NULL)
     dir = get_cwd ();

   if (file == NULL)
     {
	file = extract_file (dir);
	dirlen = (unsigned int) (file - dir);
     }
   else
     dirlen = strlen (dir);
   
   dirfile = jed_dir_file_merge (dir, file);
   if (dirfile != NULL)
     {
	char *tmp = dirfile;
	dirfile = jed_expand_link (dirfile);
	SLfree (tmp);
     }

   SLfree (b->dirfile);
   SLang_free_slstring (b->file);
   SLang_free_slstring (b->dir);
   
   if ((NULL == (b->dirfile = dirfile))
       || (NULL == (b->file = SLang_create_slstring (file)))
       || (NULL == (b->dir = SLang_create_nslstring (dir, dirlen))))
     {
	exit_error ("Out of memory", 0);
     }

#ifdef REAL_UNIX_SYSTEM
   get_inode_info (b->dir, &b->device, &b->inode);
#endif

   if (*file)
     uniquely_name_buffer (b, file);
}

/*}}}*/


/* This kills all undo information! */
int erase_buffer (void) /*{{{*/
{
   Line *beg, *tthis;

   if (-1 == jed_prepare_for_modification ())
     return -1;

   /* CLine = CBuf->end; */
   beg = CBuf->beg;
   bob();
   CLine = CLine->next; LineNum++;
   Suspend_Screen_Update = 1;
   while (CLine != NULL)
     {
	tthis = CLine;
	beg->next = tthis->next;
	tthis->prev = beg;
	
	jed_update_marks(LDELETE, 1);
	CLine = tthis->next;
	Max_LineNum--;
	free_line(tthis);
     }
   
   CLine = CBuf->beg; LineNum = 1;
   Point = 0;
   jed_update_marks(CDELETE, CLine->len);
   CLine->len = 0;
   /*   CLine->next = NULL; */
   if (CBuf->undo != NULL) delete_undo_ring(CBuf);
   CBuf->undo = NULL;
   CBuf->end = CLine;
   touch_screen_for_buffer(CBuf);
   return(1);
}

/*}}}*/

void mark_buffer_modified (Buffer *b, int modif, int with_lock) /*{{{*/
{
   if (modif)
     {
	if (b->flags & BUFFER_MODIFIED) return;
#if 0
	b->m_time = sys_time();
#endif
	b->flags |= BUFFER_MODIFIED;
	if (with_lock)
	  (void) jed_lock_buffer_file (b);
	return;
     }

   b->flags &= ~BUFFER_MODIFIED;
   if (with_lock)
     jed_unlock_buffer_file (b);
}

/*}}}*/

void jed_set_buffer_flags (Buffer *b, unsigned int flags)
{
   unsigned int modif;
   
   modif = flags & BUFFER_MODIFIED;
   b->flags &= BUFFER_MODIFIED;
   b->flags |= flags & ~BUFFER_MODIFIED;
   mark_buffer_modified (b, (int) modif, 1);
#if 0
   if ((b->flags & BUFFER_MODIFIED) != (flags & BUFFER_MODIFIED))
     mark_buffer_modified (b, flags & BUFFER_MODIFIED, 1);

   b->flags = flags;
#endif
}

void check_line() /*{{{*/
{
   if ((CLine->len == 1) && (*CLine->data == '\n') && (CLine->data != NewLine_Buffer))
     {
	SLfree ((char *)CLine->data);
	CLine->data = NewLine_Buffer;
#ifdef KEEP_SPACE_INFO
	CLine->space = 1;
#endif
     }
}

/*}}}*/


static SLang_Name_Type **find_buffer_hook (Buffer *b, char *s)
{
   if (0 == strcmp("par_sep", s)) return &b->par_sep;
   if (0 == strcmp("indent_hook", s)) return &b->indent_hook;
   if (0 == strcmp("wrap_hook", s)) return &b->wrap_hook;
   if (0 == strcmp("newline_indent_hook", s)) return &b->newline_indent_hook;
   if (0 == strcmp("bob_eob_error_hook", s)) return &b->bob_eob_error_hook;
#ifdef HAS_MOUSE
   if (0 == strcmp("mouse_down", s)) return &b->mouse_down_hook;
   if (0 == strcmp("mouse_up", s)) return &b->mouse_up_hook;
   if (0 == strcmp("mouse_drag", s)) return &b->mouse_drag_hook;
# if JED_HAS_MULTICLICK
   if (0 == strcmp("mouse_2click", s)) return &b->mouse_2click_hook;
   if (0 == strcmp("mouse_3click", s)) return &b->mouse_3click_hook;
# endif
#endif
   if (0 == strcmp("update_hook", s)) return &b->update_hook;

   return NULL;
}


int jed_unset_buffer_hook (Buffer *b, char *name)
{
   if (b == NULL) 
     return -1;
   
   if ((name != NULL) && (*name != 0))
     {
	SLang_Name_Type **nt = find_buffer_hook (b, name);
	if (nt == NULL)
	  return -1;
	
	*nt = NULL;
	return 0;
     }

   /* Otherwise, unset all of them */

   b->par_sep = NULL;
   b->indent_hook = NULL;
   b->wrap_hook = NULL;
   b->newline_indent_hook = NULL;
   b->bob_eob_error_hook = NULL;
   b->update_hook = NULL;

#ifdef HAS_MOUSE
   b->mouse_down_hook = NULL;
   b->mouse_up_hook = NULL;
   b->mouse_drag_hook = NULL;
# if JED_HAS_MULTICLICK
   b->mouse_2click_hook = NULL;
   b->mouse_3click_hook = NULL;
# endif
#endif
   
   return 0;
}

int jed_set_buffer_hook (Buffer *b, char *name, SLang_Name_Type *nt)
{
   SLang_Name_Type **ntp;

   if (b == NULL)
     return -1;

   ntp = find_buffer_hook (b, name);
   if (ntp == NULL)
     return -1;
   
   *ntp = nt;
   return 0;
}

