/*======================================================================*\
|*		Editor mined						*|
|*		auxiliary functions					*|
\*======================================================================*/

#include "mined.h"
#include "io.h"
#include <signal.h>

int panic_level = 0;		/* To adjust error handling to situation */


/*======================================================================*\
|*			Auxiliary routines				*|
\*======================================================================*/

/**
   Return the value of an environment variable.
   Do not return NULL.
 */
char *
envvar (name)
  char * name;
{
  char * val = getenv (name);
  if (val) {
	return val;
  } else {
	return "";
  }
}

/*
 * Delete file.
 */
void
delete_file (file)
  char * file;
{
#ifdef unix
  unlink (file);
#endif
#ifdef msdos
  unlink (file);
#endif
#ifdef vms
  delete (file);
#endif
}

/*
 * Panic () is called with a mined error msg and an optional system error msg.
 * It is called when something unrecoverable has happened.
 * It writes the message to the terminal, resets the tty and exits.
 * Ask the user if he wants to save his file.
 */
static
void
panic_msg (msg)
  char * msg;
{
  if (isscreenmode) {
	/* don't use status_msg here, msg may contain filename characters */
	status_line (msg, NIL_PTR);
	sleep (2);
  } else {
	(void) printf ("%s\n", msg);
  }
}

static
void
panicking (message, err, signum)
  register char * message;
  register char * err;
  register int signum;
{
  int panic_written;

  panic_level ++;
/*printf ("panicking, panic_level -> %d\n", panic_level);*/

  if (panic_level <= 2) {
	if (loading == False && modified) {
		panic_written = panicwrite ();
		if (panic_written == ERRORS) {
			sleep (2);
			build_string (text_buffer, "Error writing panic file %s", panic_file);
		} else {
			build_string (text_buffer, "Panic file %s written", panic_file);
		}
		ring_bell ();
		panic_msg (text_buffer);
	}
	if (signum != 0) {
		build_string (text_buffer, message, signum);
	} else if (err == NIL_PTR) {
		build_string (text_buffer, "%s", message);
	} else {
		build_string (text_buffer, "%s (%s)", message, err);
	}
	panic_msg (text_buffer);

	/* "normal" panic handling: */
	if (loading == False) {
		QUED ();	/* Try to save the file and quit */
		/* QUED returned: something wrong */
		sleep (2);
		panic_msg ("Aborted writing file in panic mode - trying to continue");
		panic_level --;
		return;
	}
  }

  if (panic_level <= 3) {
	if (isscreenmode) {
		set_cursor (0, YMAX);
		putchar ('\n');
#ifdef unix
		clear_window_title ();
#endif
		raw_mode (False);
	}
	delete_yank_files ();
  }
  exit (1) /* abort () sends IOT which would again be caught */;
}

void
panic (message, err)
  register char * message;
  register char * err;
{
  panicking (message, err, 0);
}

void
panicio (message, err)
  register char * message;
  register char * err;
{
/* Should panic_level already be increased here ? */
  panic (message, err);
}

/*
 * Bad_write () is called when a write failed. Notify the user.
 */
void
bad_write (fd)
  int fd;
{
  if (fd == output_fd) {	/* Cannot write to terminal? */
	panicio ("Write error on terminal", serror ());
  }

  ring_bell ();
  error2 ("Write aborted (File incomplete): ", serror ());
}


void
catch_interrupt (signum)
  int signum;
{
  catch_signals ((signalfunc) catch_interrupt);
  if (signum == SIGTERM && panic_level == 0) {
	panic_level ++;
/*printf ("catch_interrupt -> EXMINED\n");*/
	EXMINED ();
	panic_level --;
  } else {
/*printf ("catch_interrupt -> panicking\n");*/
	panicking ("External signal %d caught - terminating", NIL_PTR, signum);
  }
}

/*-------------------------------------------------------------------------*/

/*
 * Memory allocation
 */
#ifdef msdos
# ifdef __TURBOC__
#include <alloc.h>
#define allocate farmalloc
#define freemem farfree
# else
extern void * malloc ();
extern void free ();
#define allocate malloc
#define freemem free
# endif
#else
extern void * malloc ();
extern void free ();
#define allocate malloc
#define freemem free
#endif

#define dont_debug_out_of_memory
#ifdef debug_out_of_memory
static int allocount = 0;
#endif

void *
alloc (bytes)
  int bytes;
{
#ifdef debug_out_of_memory
  allocount ++;
  if (allocount == 500) {
	return NIL_PTR;
  }
#endif
  return allocate ((unsigned) bytes);
/*
  char * p;

  if ((p = allocate ((unsigned) bytes)) == NIL_PTR) {
	panic ("Out of memory", NIL_PTR);
  }
  return p;
*/
}

void
free_space (p)
  void * p;
{
  freemem (p);
}

/*
 * free header list
 */

#define pointersize	sizeof (void *)
#define blocksizeof(typ) ((sizeof (typ) + pointersize - 1) / pointersize * pointersize)

LINE * free_header_list = NIL_LINE;

static
void
alloc_headerblock (n)
  int n;
{
  LINE * new_header;
  LINE * new_list;
  int i = 0;

  new_list = alloc (n * blocksizeof (LINE));
  if (new_list == NIL_LINE) {
	free_header_list = NIL_LINE;
  } else {
	while (i < n) {
		new_header = (LINE *) ((long) new_list + i * blocksizeof (LINE));
		new_header->next = free_header_list;
		free_header_list = new_header;
		i ++;
	}
  }
}

LINE *
alloc_header ()
{
/*  return alloc (sizeof (LINE)); */
  LINE * new_header;

  if (free_header_list == NIL_LINE) {
	alloc_headerblock (64);
	if (free_header_list == NIL_LINE) {
		alloc_headerblock (16);
		if (free_header_list == NIL_LINE) {
			alloc_headerblock (4);
			if (free_header_list == NIL_LINE) {
				alloc_headerblock (1);
				if (free_header_list == NIL_LINE) {
					return NIL_LINE;
				}
			}
		}
	}
  }
  new_header = free_header_list;
  free_header_list = free_header_list->next;
  return new_header;
}

void
free_header (hp)
  LINE * hp;
{
/*  freemem (hp); */
  hp->next = free_header_list;
  free_header_list = hp;
}

/*
 * Unnull () changes a NULL string pointer into an empty string pointer 
 * to allow easy feading of string results into build_string / sprintf
 */
char *
unnull (s)
  char * s;
{
  if (s == NIL_PTR) {
	return "";
  } else {
	return s;
  }
}

/*
 * Output an (unsigned) long in a 10 digit field without leading zeros.
 * It returns a pointer to the first digit in the buffer.
 */
char *
num_out (number, radix)
  long number;
  long radix;
{
  static char num_buf [11];		/* Buffer to build number */
  register long digit;			/* Next digit of number */
  register long pow;			/* Highest radix power of long */
  FLAG digit_seen = False;
  int i;
  int maxdigs;
  char * bufp = num_buf;

  if (radix == 16) {
	pow = 268435456L;
	maxdigs = 8;
  } else {
	pow = 1000000000L;
	maxdigs = 10;
	if (number < 0) {
		* bufp ++ = '-';
		number = - number;
	}
  }

  for (i = 0; i < maxdigs; i ++) {
	digit = number / pow;		/* Get next digit */
	if (digit == 0L && digit_seen == False && i != 9) {
	} else {
		if (digit > 9) {
			* bufp ++ = 'A' + (char) (digit - 10);
		} else {
			* bufp ++ = '0' + (char) digit;
		}
		number -= digit * pow;	/* Erase digit */
		digit_seen = True;
	}
	pow /= radix;			/* Get next digit */
  }
  * bufp = '\0';
  return num_buf;
}

char *
dec_out (number)
  long number;
{
  return num_out (number, 10);
}

/*
 * Build_string () prints the arguments as described in fmt, into the buffer.
 * %s indicates a string argument, %d indicates an integer argument.
 */
#ifndef build_string /* otherwise build_string is sprintf */

static
char *
hex_out (number)
  long number;
{
  return num_out (number, 16);
}

/* VARARGS */
void
build_string (buf, fmt, args)
  register char * buf;
  register char * fmt;
  int args;
{
  int * argptr = & args;
  char * scanp;
  FLAG islong;
  int length = 0;

  while (* fmt) {
     if (* fmt == '%') {
	fmt ++;
	fmt = scan_int (fmt, & length);
	if (* fmt == 'l') {
		islong = True;
		fmt ++;
	} else {
		islong = False;
	}
	switch (* fmt ++) {
	case 's' :
		scanp = (char *) * argptr;
		break;
	case 'd' :
		if (islong) {
			scanp = dec_out ((long) * ((long *) argptr));
			if (sizeof (long) > sizeof (int)) {
				argptr ++;
			}
		} else {
			scanp = dec_out ((long) * argptr);
		}
		break;
	case 'D' :
		scanp = dec_out ((long) * ((long *) argptr));
		if (sizeof (long) > sizeof (int)) {
			argptr ++;
		}
		break;
	case 'x' :
	case 'X' :
		scanp = hex_out ((long) * ((long *) argptr));
		if (sizeof (long) > sizeof (int)) {
			argptr ++;
		}
		break;
	default :
		scanp = "";
	}
	while ((* buf ++ = * scanp ++))
		{}
	buf --;
	argptr ++;
     } else {
	* buf ++ = * fmt ++;
     }
  }
  * buf = '\0';
}

#endif /* ndef build_string */

/*
 * scan_int () converts a string into a natural number
 * returns the character pointer behind the last digit
 */
char *
scan_int (str, nump)
  char * str;
  int * nump;
{
  register char * chpoi = str;
  int negative = False;

  while (* chpoi == ' ') {
	chpoi ++;
  }
  if (* chpoi == '-') {
	negative = True;
	chpoi ++;
  }
  if (* chpoi >= '0' && * chpoi <= '9') {
	* nump = 0;
  } else {
	return str;
  }
  while (* chpoi >= '0' && * chpoi <= '9' && quit == False) {
	* nump *= 10;
	* nump += * chpoi - '0';
	chpoi ++;
  }
  if (negative) {
	* nump = - * nump;
  }
  return chpoi;
}

/*
 * copy_string () copies the string 'from' into the string 'to' which 
   must be long enough
 */
void
copy_string (to, from)
  register char * to;
  register char * from;
{
  while ((* to ++ = * from ++) != '\0')
	{}
}

/*
 * length_of () returns the number of bytes in the string 'string'
 * excluding the '\0'.
 */
int
length_of (string)
  register char * string;
{
  register int count = 0;

  if (string != NIL_PTR) {
	while (* string ++ != '\0') {
		count ++;
	}
  }
  return count;
}


/*======================================================================*\
|*			System error retrieval				*|
\*======================================================================*/

#ifdef __GNUC__

#include <errno.h>

#else	/* ifdef __GNUC__ */

#ifdef vms

/* #define includeerrno */
#  ifdef includeerrno
#  include <errno.h>
#  else
    extern volatile int noshare errno;
    extern volatile int noshare vaxc$errno; /* VMS error code when errno = EVMSERR */
#  define EVMSERR 65535
    extern volatile int noshare sys_nerr;
    extern volatile char noshare * sys_errlist [];
#  endif

#else	/* #ifdef vms */

#ifdef __CYGWIN__

#include <errno.h>

/* What's the purpose of this silly API renaming game ? */
#define sys_nerr _sys_nerr
#define sys_errlist _sys_errlist
/*const char *const *sys_errlist = _sys_errlist;*/
/* from a mail => initializer element is not constant */

#else	/* #ifdef __CYGWIN__ */

#if (defined(BSD) && BSD >= 199306)

#include <errno.h>

#else

# ifdef __linux__
#    include <errno.h>
# endif

  extern int errno;
  extern int sys_nerr;

#  ifndef __USE_BSD
#   ifndef __USE_GNU
/* Why did those Linux kernel hackers have to change the interface here, 
   inserting a completely superfluous "const"?
   All this voguish "assumedly-modern C" crap only increases 
   portability problems and wastes developers' time. 
   It's nothing but a nuisance. */
  extern char * sys_errlist [];
#   endif
#  endif

#endif	/* #else BSD >= 199306 */

#endif	/* #else __CYGWIN__ */

#endif	/* #else vms */

#endif	/* #else __GNUC__ */


/*
 * serrorof delivers the error message of the given errno value.
 * serror delivers the error message of the current errno value.
 * geterrno just returns the current errno value.
 */

char *
serrorof (errnum)
  int errnum;
{
#ifdef __GNUC__
  return strerror (errnum);
#else
  if ((errnum < 0) || (errnum >= sys_nerr)) {
	  static char s [20];
#ifdef vms
	  if (errnum == EVMSERR)
	     build_string (s, "VMS error %d", vaxc$errno);
	  else
#endif
	     build_string (s, "Unknown error %d", errnum);
	  return s;
  } else {
	return (char *) sys_errlist [errnum];
  }
/* Without the (char *) cast, on some systems, the compiler emits stupid 
   warnings about some "const" crap, due to the nonsense 
   that I complained about above */
#endif
}

char *
serror ()
{
  return serrorof (errno);
}

int
geterrno ()
{
  return errno;
}


/*======================================================================*\
|*				End					*|
\*======================================================================*/
