/***************************************************************************\
|*									   *|
|*  utils.c:	A version of Tetris to run on ordinary terminals,	   *|
|*		(ie., not needing a workstation, so should available	   *|
|*		to peasant Newwords+ users.  This module supplies all	   *|
|*		the function that I will need, but which are not	   *|
|*		intrinsically a part of Tetris itself.			   *|
|*									   *|
|*  Author:	Mike Taylor (mirk@uk.ac.warwick.cs)			   *|
|*  Started:	Fri May 26 12:26:05 BST 1989				   *|
|*									   *|
\***************************************************************************/

#include <varargs.h>
#include <curses.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>

/* Stuff needed to run under Linux */
char* l_rindex (const char* s, int c)
{return strrchr (s, c);}

#include "tt.h"
#include "utils.h"
#include "screen.h"

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

extern char *rindex ();
extern char *getenv ();

/***************************************************************************\
|*									   *|
|*  The function basename() acts like the UNIX(tm) command of the same	   *|
|*  name.  It takes as its argument a string, and returns the last path-   *|
|*  component of that string, (ie, the section after the last '/'), or	   *|
|*  the whole string if it has no '/'.					   *|
|*									   *|
\***************************************************************************/

char *basename (name)
  char *name;
{
  char *slash;
  if (slash = l_rindex (name, '/'))
    return (slash+1);
  else
    return (name);
}

/***************************************************************************\
|*									   *|
|*  This is my home-made varargs-based version of C++'s neat function of   *|
|*  the same name.  It saves all that tedious mucking about with static	   *|
|*  buffers which you sprintf into, then forget all about, by basically	   *|
|*  being a sprintf with its own statis storage.  It takes as arguments	   *|
|*  exactly the same things as printf(3), and returns a pointer to the	   *|
|*  resultant string.							   *|
|*									   *|
\***************************************************************************/

/*VARARGS*/
char *form (va_alist)
  va_dcl
{
  va_list pvar;
  char *fmt_string;
  static char result[LINELEN];
  
  va_start (pvar);
  fmt_string = va_arg (pvar, char*);
  (void) vsprintf (result, fmt_string, pvar);
  va_end (pvar);
  return (result);
}

/***************************************************************************\
|*									   *|
|*  This is yer bog-standard "print a message and quit" function, except   *|
|*  that it checks to see if we are in curses(3x), and if so, takes us	   *|
|*  out before doing its stuff.	 The arguments are an integer, the exit	   *|
|*  status, and a string containing an error report, to which will be	   *|
|*  prepended the program name when it is printed.  If the status code	   *|
|*  is LE_OK, (ie. nothing went wrong), no message is printed.		   *|
|*									   *|
\***************************************************************************/

void die (status, line)
  int status;
  char *line;
{
  if (in_curses) {
    myrefresh ();
#ifndef LINT
    nocbreak ();
    echo ();
#endif /* LINT */
    endwin ();
  }

  if (status != LE_OK)
    (void) printf ("%s: %s\n", prog_name, line);

#ifndef LOCKF
  (void) unlink (LOCK_FILE);	/* Just in case :-) */
#endif /* LOCKF */
  exit (status);
}

/***************************************************************************\
|*									   *|
|*  The function get_termcap extracts the information we need from the	   *|
|*  UNIX terminal capability database.	It has been (correctly) pointed	   *|
|*  out that most of what I do in this function is already done for me	   *|
|*  by curses(3x), but (A) curses behaves stupidly on terminals with	   *|
|*  termcap's "sg" non-zero, (ie. refuses to enter standout mode, so	   *|
|*  you *have* to do it manually), and (B) I wanted to prove a point to	   *|
|*  Sunny and others, who said it was impossible.  Tgets() sucks :-P	   *|
|*									   *|
\***************************************************************************/

void get_termcap ()
{
  static char bp[1024];		/* Seems odd to hardwire the 1024, but ... */
  char *term_name;		/* ... the manual entry told me to do it! */
  int status;			/* Success/failure of tgetent here */
  char *tmp_ptr = tc_string;	/* Pointer to be advanced by tgetstr() */

  if ((term_name = getenv ("TERM")) == NULL)
    die (LE_ENV, "couldn't get TERM from environment");

  if ((status = tgetent (bp, term_name)) == -1)
    die (LE_TERMCAP, "couldn't open TERMCAP file");

  if (status == 0)
    die (LE_TERMCAP, "couldn't find your terminal in TERMCAP file");

  if ((screen_depth = tgetnum ("li")) == -1)
    die (LE_TERMCAP, "couldn't get screen-depth from termcap");

  if ((screen_width = tgetnum ("co")) == -1)
    die (LE_TERMCAP, "couldn't get screen-width from termcap");

  if ((so_gunk = tgetnum ("sg")) == -1)
    so_gunk = 0;		/* Default value */

  so_str = tmp_ptr;
  if (tgetstr ("so", &tmp_ptr) == NULL)
    die (LE_TERMCAP, "couldn't get standout code from termcap");

  se_str = tmp_ptr;
  if (tgetstr ("se", &tmp_ptr) == NULL)
    die (LE_TERMCAP, "couldn't get standend code from termcap");
}

/***************************************************************************\
|*									   *|
|*  The function flush_keybord() checks to see whether there are any	   *|
|*  keystrokes waiting to be read, and if so reads them and throws	   *|
|*  them away.	Surprising how complex this needs to be, but the only	   *|
|*  alternative I can think of is to ioctl() stdin to be non-blocking,	   *|
|*  read() until it returns 0, then ioctl() it back, which is scarcely	   *|
|*  an improvement.							   *|
|*									   *|
\***************************************************************************/

void flush_keyboard ()
{
  fd_set fds;			/* Will contain only stdin */
  struct timeval timeout;	/* Will contain a zero timeout (poll) */
  static char buffer[LINELEN];	/* Will be used as the buffer to read(2) */
  int status;			/* Contains select(2)'s return value */

  while (1) {
    FD_ZERO (&fds);
    FD_SET (0, &fds);
    timeout.tv_sec = 0L;
    timeout.tv_usec = 0L;
    if (((status = select (1, &fds, (fd_set*) NULL, (fd_set*) NULL,
			   &timeout)) == -1) && (errno != EINTR))
      die (LE_SELECT, "select(2) failed in flush_keybord()");
    if ((status == 0) || (status == -1))
      break;			/* Can only be -1 if errno == EINTR */
    (void) read (0, buffer, LINELEN);
  }
}

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