/*************************************************************************
** ^FILE: winsize.c - implement the routine get_winsize()
**
** ^DESCRIPTION:
**    Implement the get_winsize() function for various windowing and
**    operating systems.
**
** ^HISTORY:
**    10/01/90 	Brad Appleton 	<brad@ssd.csd.harris.com> 	Created
***^^**********************************************************************/


/***************************************************************************
** ^FUNCTION: get_winsize - get the current window size
**
** ^SYNOPSIS:
**    void get_winsize( fd, nrows, ncols )
**
** ^PARAMETERS:
**    int fd;
**    -- file-descriptor associated with the "output-window"
**
**    int *nrows;
**    -- pointer to number of rows in window
**
**    int *ncols;
**    -- pointer to number of columns in window
**
** ^DESCRIPTION:
**    Get_winsize will attempt to determine the maximum number of
**    rows and colums that will fit on one screen-full of the associated
**    output device. If it fails to do this, it will assume the "window"
**    is a 24x80 (rows by columns) display.
**
** ^REQUIREMENTS:
**    fd must correspond to a valid output device.
**
** ^SIDE-EFFECTS:
**    The memory addressed by nrows and ncols is assigned the corresponding
**    appropriate value(s).
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**     System dependent.
***^^**********************************************************************/

#include <stdio.h>

#ifdef __ANSI_C__
# define  GET_WINSIZE(fd,nrows,ncols)  \
	void get_winsize( int fd, int *nrows, int *ncols )
#else
# define  GET_WINSIZE(fd,nrows,ncols)  \
	void get_winsize( fd, nrows, ncols ) \
	int fd, *nrows, *ncols;   /* nrows and ncols are passed by reference */
#endif

#define  DEFAULT_ROWS  24
#define  DEFAULT_COLS  80

#ifdef vms

#include <stdio.h>
#include <iodef.h>
#include <ssdef.h>
#include <descrip.h>
#include <useful.h>

   /* structure to contain terminal characteristics */
typedef  struct {
      short garb1, cols;
      char   garb2, garb3, garb4, rows;
} termchar_t;

int sys$assign();
int sys$qiow();
int sys$dassgn();

GET_WINSIZE( fd, nrows, ncols )
{
   int c, charlen = 8;
   termchar_t   termchar;
   int chan;
   $DESCRIPTOR( devnam,"SYS$COMMAND" );

   sys$assign( &devnam, &chan, 0, 0 );
   sys$qiow( 0, chan, IO$_SENSEMODE, 0, 0, 0, &termchar, &charlen, 0, 0, 0, 0 );
   sys$dassgn( chan );

   *nrows = ( termchar.rows > 0 ) ? (int) termchar.rows : DEFAULT_ROWS;
   *ncols = ( termchar.cols > 0 ) ? (int) termchar.cols : DEFAULT_COLS;
}

#else
#ifdef unix

   /*
   ** we will either try to access terminfo through the termcap-interface
   ** in the curses library (which would require linking with -lcurses)
   ** or use termcap directly (which would require linking with -ltermcap)
   */
#ifndef USE_TERMCAP
#if ( defined(USE_TERMINFO) || defined(USE_CURSES) )
#define USE_TERMCAP
#endif
#endif


#ifdef USE_TERMCAP
# define TERMBUFSIZ    1024
# define UNKNOWN_TERM  "unknown"
# define DUMB_TERMBUF  "dumb:co#80:hc:"
extern int  tgetent();
extern int  tgetnum();
#endif


   /* try to get TIOCGWINSZ from termios.h, then from sys/ioctl.h */
#include <termios.h>
#if ( !defined(TIOCGWINSZ)  &&  !defined(TIOCGSIZE)  &&  !defined(WIOCGETD) )
#include <sys/ioctl.h>
#endif

   /* if still dont have TIOCGWINSZ (or TIOCGSIZE) try for WIOCGETD */
#if ( !defined(TIOCGWINSZ)  &&  !defined(TIOCGSIZE)  &&  !defined(WIOCGETD) )
#include <sgtty.h>
#endif

#include <useful.h>

/*
** get_winsize() -- determine # of rows/columns that will fit on the screen.
**
**     The environment variables $LINES and $COLUMNS will be used if they exist.
**     If not, then the TIOCGWINSZ call to ioctl() is used (if it is defined).
**     If not, then the TIOCGSIZE call to ioctl() is used (if it is defined).
**     If not, then the WIOCGETD call to ioctl() is used (if it is defined).
**     If not, then get the info from terminfo/termcap (if it is there)
**     Otherwise, assume we have a 24x80 screen.
*/

extern  int   ioctl();
extern  int   isatty();
extern  long  atol();
extern  char *getenv();

GET_WINSIZE( fd, nrows, ncols )
{
   char  *lines_env, *cols_env;
   long  lrow = 0, lcol = 0;
   int   lines = 0, cols = 0;
#ifdef USE_TERMCAP
   char  term_buf[ TERMBUFSIZ ], *term_env;
#endif
#ifdef TIOCGWINSZ
   struct winsize  win;
#else
#ifdef TIOCGSIZE
   struct ttysize  win;
#else
#ifdef WIOCGETD
   struct uwdata  win;
#endif
#endif
#endif

      /* make sure that fd corresponds to a terminal */
   if ( !isatty( fd ) ) {
      *nrows = DEFAULT_ROWS;
      *ncols = DEFAULT_COLS;
      return;
   }

      /* LINES & COLUMNS environment variables override everything else */
   lines_env = getenv( "LINES" );
   if ( lines_env  &&  (lrow = atol(lines_env)) > 0 ) {
      *nrows = lines = (int) lrow;
   }

   cols_env = getenv( "COLUMNS" );
   if ( cols_env  &&  (lcol = atol(cols_env)) > 0 ) {
      *ncols = cols = (int) lcol;
   }

#ifdef TIOCGWINSZ
      /* see what ioctl() has to say (overrides terminfo & termcap) */
   if ( (!lines || !cols)  &&  ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
      if ( !lines  &&  win.ws_row > 0 ) {
         *nrows = lines = (int) win.ws_row;
      }

      if ( !cols  &&  win.ws_col > 0 ) {
         *ncols = cols = (int) win.ws_col;
      }
   }/*if*/
#else
#ifdef TIOCGSIZE
      /* see what ioctl() has to say (overrides terminfo & termcap) */
   if ( (!lines || !cols)  &&  ioctl(fd, TIOCGSIZE, &win) != -1 ) {
      if ( !lines  &&  win.ts_lines > 0 )
         *nrows = lines = (int) win.ts_lines;

      if ( !cols  &&  win.ts_cols > 0 )
         *ncols = cols = (int) win.ts_cols;
   }/*if*/
#else
#ifdef WIOCGETD
      /* see what ioctl() has to say (overrides terminfo & termcap) */
   if ( (!lines || !cols)  &&  ioctl(fd, WIOCGETD, &win) != -1 ) {
      if ( !lines  &&  win.uw_height > 0 )
         *nrows = lines = (int) (win.uw_height / win.uw_vs);

      if ( !cols  &&  win.uw_width > 0 )
         *ncols = cols = (int) (win.uw_width / win.uw_hs);
   }/*if*/
#endif
#endif
#endif

#ifdef USE_TERMCAP
      /* see what terminfo/termcap has to say */
   if ( !lines || !cols ) {
      if ( !(term_env = getenv("TERM")) )
         term_env = UNKNOWN_TERM;

      if ( (tgetent(term_buf, term_env) <= 0) )
         strcpy( term_buf, DUMB_TERMBUF );

          if ( !lines   &&   (lrow = tgetnum("li")) > 0 )
            *nrows = lines = (int) lrow;

          if ( !cols   &&   (lcol = tgetnum("co")) > 0 )
            *ncols = cols = (int) lcol;
   }
#endif

      /* use 80x24 if all else fails */
   if ( !lines )  *nrows = DEFAULT_ROWS;
   if ( !cols )   *ncols = DEFAULT_COLS;

}/*get_winsize()*/

#else

GET_WINSIZE( fd, nrows, ncols )
{
      /* just use 80x24 */
   *nrows = DEFAULT_ROWS;
   *ncols = DEFAULT_COLS;
}

#endif  /* not a vms system */
#endif  /* not a unix-system */

#ifdef WINSIZE_TEST

#ifdef vms
# include <ssdef.h>
#endif

   /* test get_winsize to see if it works */
main()
{
   int rows, cols;

   get_winsize( fileno(stderr), &rows, &cols );
   printf( "Output will be %d rows by %d columns\n", rows, cols );

#ifdef vms
   exit( SS$_NORMAL );
#else
   exit( 0 );
#endif

}

#endif
