/*
 * Program:	Operating system dependent routines - System V R 3
 *
 *  WARNING -- this is an incomplete half modified version for System V R 3
 *               
 *
 * Author:	Michael Seibel
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: mikes@cac.washington.edu
 *
 * Date:	6 Jan 1992
 * Last Edited:	11 May 1992
 *
 * Copyright 1991 by the University of Washington
 *
 *  Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Notes:
 *
 * SGI IRIX 4.0.1 port by:
 *       johnb@edge.cis.mcmaster.ca,  2 April 1992
 *
 * Dynix/PTX port by:
 *       Donn Cave, UCS/UW, 15 April 1992
 *
 *
 */

#include 	<stdio.h>
#include	<errno.h>
#include	<setjmp.h>
#include	<time.h>
#include	<pwd.h>

#include	"osdep.h"
#include	"estruct.h"
#include        "edef.h"
#include        "pico.h"

#ifdef	POSIX
#include	<termios.h>

struct termios nstate,
		ostate;
#else
#if	defined(sv3) || defined(sgi)
#include	<termio.h>

struct termio nstate,
              ostate;

#else
struct  sgttyb  ostate;          /* saved tty state */
struct  sgttyb  nstate;          /* values for editor mode */
struct tchars	otchars;	/* Saved terminal special character set */
struct tchars	ntchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
				/* A lot of nothing */
#endif	/* sv3 || sgi */
#endif	/* POSIX */

extern	int rtfrmshell();	/* return from suspended shell */

jmp_buf	alrm_state;		/* stack env to handle SIGALRM */


#if	defined(nxt) || defined(aix) || defined(POSIX)
#define	SIGTYPE	void
#else
#define	SIGTYPE int
#endif

SIGTYPE	do_hup_signal();
SIGTYPE	do_alarm_signal();
#ifdef	TIOCGWINSZ
SIGTYPE	winch_handler();
#endif


/*
 * This function is called once to set up the terminal device streams.
 */
ttopen()
{
	if(Pmaster == NULL){
#ifdef	POSIX
	    tcgetattr (0, &ostate);
	    tcgetattr (0, &nstate);
	    nstate.c_lflag &= ~(ISIG | ICANON | ECHO);
	    nstate.c_iflag &= ~ICRNL;
	    nstate.c_oflag &= ~(ONLCR | OPOST);
	    nstate.c_cc[VMIN] = 1;
	    nstate.c_cc[VTIME] = 0;
	    tcsetattr (0, TCSADRAIN, &nstate);
#else
#if	defined(sv3) || defined(sgi)
	    (void) ioctl(0, TCGETA, &ostate);
	    (void) ioctl(0, TCGETA, &nstate);    /** again! **/

	    nstate.c_lflag &= ~(ICANON | ISIG | ECHO);	/* noecho raw mode  */
	    nstate.c_oflag &= ~(OPOST | ONLCR);
	    nstate.c_iflag &= ~INLCR;
	    
	    nstate.c_cc[VMIN] = '\01';  /* minimum # of chars to queue  */
	    nstate.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
	    (void) ioctl(0, TCSETA, &nstate);
#else
	    ioctl(0, TIOCGETP, &ostate);	/* save old state */
	    ioctl(0, TIOCGETP, &nstate);	/* get base of new state */
	    nstate.sg_flags |= RAW;
	    nstate.sg_flags &= ~(ECHO|CRMOD);	/* no echo for now... */
	    ioctl(0, TIOCSETP, &nstate); 	/* set mode */
	    
	    ioctl(0, TIOCGETC, &otchars); 	/* Save old characters */
	    ioctl(0, TIOCSETC, &ntchars); 	/* put new character into K */
#endif	/* sv3 */
#endif	/* POSIX */
	}

	signal(SIGHUP, (SIGTYPE *)do_hup_signal); /* deal with SIGHUP */
	signal(SIGALRM, (SIGTYPE *)do_alarm_signal); /* timer for new mail */
#ifdef	TIOCGWINSZ
        signal(SIGWINCH, (SIGTYPE *)winch_handler); /* window size changes */
#endif
#ifndef	sv3
	signal(SIGTSTP, (SIGTYPE *)SIG_DFL);	/* set signals so we can */
	signal(SIGCONT, (SIGTYPE *)rtfrmshell);	/* suspend & restart emacs */
#endif
}



/*
 * This function gets called just before we go back home to the command
 * interpreter.
 */
ttclose()
{
    if(Pmaster){
	signal(SIGHUP, SIG_DFL);
	signal(SIGALRM, SIG_DFL);
#ifndef	sv3
	signal(SIGCONT, SIG_DFL);
#endif
#ifdef	TIOCGWINSZ
	signal(SIGWINCH, SIG_DFL);
#endif
    }
    else{
#ifdef	POSIX
	tcsetattr (0, TCSADRAIN, &ostate);
#else
#if	defined(sv3) || defined(sgi)
        ioctl(0, TCSETA, &ostate);
#else
	ioctl(0, TIOCSETP, &ostate);
	ioctl(0, TIOCSETC, &otchars);
#endif	/* sv3 || sgi */
#endif	/* POSIX */
    }
}


/*
 * Write a character to the display. 
 */
ttputc(c)
{
	putc(c, stdout);
}


/*
 * Flush terminal buffer. Does real work where the terminal output is buffered
 * up. A no-operation on systems where byte at a time terminal I/O is done.
 */
ttflush()
{
        fflush(stdout);
}

/*
 * Read a character from the terminal, performing no editing and doing no echo
 * at all.
 */
ttgetc()
{
    register int c;

    if((c = fgetc(stdin)) == EOF){
	if(errno == EINTR)		/* only one that might be OK */
	  return(NODATA);
	kill(getpid(), SIGHUP);		/* the only honorable thing to do */
    }
    else
      return(c & 0xff);
}


#if	TYPEAH
/* typahead:	Check to see if any characters are already in the
 *		keyboard buffer
 */
typahead()
{
	int x;	/* holds # of pending chars */

	return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
}
#endif


/*
 * Read in a key.
 * Do the standard keyboard preprocessing. Convert the keys to the internal
 * character set.  Resolves escape sequences and returns no-op if global
 * timeout value exceeded.
 */
GetKey()
{
	int    c;

	if(timeout){
	    if(setjmp(alrm_state) == 0){
		alarm((unsigned)timeout);
		errno = 0;
		c = (*term.t_getchar)();
		if(errno == EINTR)
		  c = NODATA;
	    }
	    else
	      c = NODATA; 		/* alarm went off so go back */

	    alarm(0);			/* shut off the alarm */
	    if(c == NODATA)
	      return(c);
	}
	else{
	    c = (*term.t_getchar)();
	}

	if (c == METACH) {                      /* Apply M- prefix      */
	    int status;
	    
	    /*
	     * this code should intercept special keypad keys
	     */

	    switch(status = kbseq(&c)){
	      case 0 : 	/* no dice */
    		return(c);
	      case  K_PAD_UP		:
	      case  K_PAD_DOWN		:
	      case  K_PAD_RIGHT		:
	      case  K_PAD_LEFT		:
	      case  K_PAD_PREVPAGE	:
	      case  K_PAD_NEXTPAGE	:
	      case  K_PAD_HOME		:
    		return(status);
	      case F1  :
	      case F2  :
	      case F3  :
	      case F4  :
	      case F5  :
	      case F6  :
	      case F7  :
	      case F8  :
	      case F9  :
	      case F10 :
	      case F11 :
	      case F12 :
    		return(status);
	      case BADESC :
	      default : 			/* punt the whole thing	*/
    		(*term.t_beep)();
    		break;
	    }
        }

        if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
          c = CTRL | (c+'@');

	    return (c);

}



/* kbseq - looks at an escape sequence coming from the keyboard and 
 *         compares it to a trie of known keyboard escape sequences, and
 *         performs the function bound to the escape sequence.
 * 
 *         returns: BADESC, the escaped function, or 0 if not found.
 */
kbseq(c)
int	*c;
{
	register char	b;
	register int	first = 1;
	register struct	KBSTREE	*current = kpadseqs;

	if(kpadseqs == NULL)				/* bag it */
		return(BADESC);
	while(1){
		*c = b = (*term.t_getchar)();

		while(current->value != b){
		    if(current->left == NULL){		/* NO MATCH */
			if(first)
			  return(BADESC);
			else
			  return(0);
		    }
		    current = current->left;
		}

		if(current->down == NULL)		/* match!!!*/
		  return(current->func);
		else
		  current = current->down;

		first = 0;
	}
}



/*
 * This is code for a planned pine feature that isn't actually in use 
 * yet.  It is an example of one possible way of implementing an alternate
 * editor, but maybe not the final word on how it will be done.  
 * It may be that we decide to make this a configuration option (thus 
 * saving a very limited resource: control-key combinations.  We're 
 * still considering these options...
 */
#include <sys/wait.h>


/*
 * alt_editor - fork off an alternate editor for mail message composition
 */
alt_editor(f, n)
{
#ifdef OLDWAY
    char   eb[NLINE];
#else
    char   *eb;
#endif
    char   *fn;
    char   *writetmp();
    int	   child, pid, done = 0;
    long   l;
#ifdef POSIX
    int    stat;
#else
    union  wait stat;
#endif
    FILE   *p;

#ifdef OLDWAY
    *eb = '\0';
#endif

    if(Pmaster == NULL)
      return;

#ifdef OLDWAY
    while(!done){
	/* which alternate editor */
	switch(mlreply("Which alternate editor ? ", eb, NLINE, TRUE)){
	  case ABORT:
	    return(-1);
	  case HELPCH:
	    emlwrite("no alternate editor help yet");

/* take sleep and break out after there's help */
	    sleep(3);
	    break;
	  case (CTRL|'L'):
	    sgarbf = TRUE;
	    update();
	    break;
	  case TRUE:
	  case FALSE:					/* does editor exist ? */
	    if(*eb== '\0'){				/* leave silently */
		emlwrite("");
		return(-1);
	    }
#ifdef MAYBELATER
	    else if(*eb != '/'){
		int status;
		char *fp, *efp;

		if(*eb == '~'){
		    strcpy(s, "~");
		    fixpath(s, 1024);
		    strcat(s,"/");
		    strcat(eb,"/");
		}
		else if((fp=getenv("PATH")) == NULL){
		    emlwrite("Can't find editor \"%s\"", eb);
		    return(-1);
		}
else
 emlwrite("got path: %s",fp);
		while(fp){
		    sprintf(s, "%s/%s", fp, eb);
		    if((status=fexist(eb, "x", &l)) != FIOSUC){
			fioperr(status, eb);
			return(-1);
		    }
		}
	    }
#endif
	    done++;
	    break;
	  default:
	    break;
	}
    }
#else
    if(Pmaster->alt_ed == NULL)
      return;

    eb = Pmaster->alt_ed;
#endif

    if((fn=writetmp(0, 1)) == NULL){	/* get temp file */
	emlwrite("Problem writing temp file for alt editor");
	return(-1);
    }

    if(Pmaster)
      (*Pmaster->raw_io)(0);		/* turn off raw mode */

    if(child=fork()){			/* wait for the child to finish */
	while((pid=wait(&stat)) != child)
	  ;
    }
    else{				/* spawn editor */
	if(execlp(eb, eb, fn, (char *) 0) < 0)
	  exit(1);
    }

    if(Pmaster)
      (*Pmaster->raw_io)(1);		/* turn on raw mode */

    /*
     * replace edited text with new text 
     */
    curwp->w_bufp->b_flag &= ~BFCHG;	/* make sure it gets blasted */
    bclear(curwp->w_bufp);		/* blast old text */
    readin(fn, 0);			/* read new text */
    unlink(fn);				/* blast temp file */

    ttopen();
    refresh(0, 1);			/* redraw */
}



/*
 *  bktoshell - suspend and wait to be woken up
 */
bktoshell()		/* suspend MicroEMACS and wait to wake up */
{
#ifndef	sv3
	int pid;
	int rtfrmshell();
	extern int vttidy();

        if(!(gmode&MDSSPD)){
	    emlwrite("\007Unknown command: ^Z");
            return;
	}

	if(Pmaster){
	    (*Pmaster->raw_io)(0);	/* actually in pine source */

	    movecursor(term.t_nrow, 0);
	    printf("\n\n\nUse \"fg\" to return to Pine\n");

	    signal(SIGTSTP,SIG_DFL);	/* set signals so that we can */
	    signal(SIGCONT, (SIGTYPE *)rtfrmshell); /* suspend & restart emacs */
	}
	else{
	    vttidy();
	}

        movecursor(term.t_nrow, 0);
        peeol();
        (*term.t_flush)();

	pid = getpid();
	kill(pid,SIGTSTP);
#else
#endif
}


/* 
 * rtfrmshell - back from shell, fix modes and return
 */
rtfrmshell()
{
#ifndef	sv3 
        if(Pmaster){
	    signal(SIGCONT, SIG_DFL);
	    if(Pmaster)
	      (*Pmaster->raw_io)(1);		/* actually in pine source */
	    (*Pmaster->keybinit)(gmode&MDFKEY);	/* using f-keys? */
	}

	ttopen();

	sgarbf = TRUE;
	curwp->w_flag = WFHARD;
	refresh();
#else
#endif
}



/*
 * do_alarm_signal - jump back in the stack to where we can handle this
 */
SIGTYPE do_alarm_signal()
{
    signal(SIGALRM, (SIGTYPE *)do_alarm_signal);
    longjmp(alrm_state, 1);
}



/*
 * do_hup_signal - jump back in the stack to where we can handle this
 */
SIGTYPE do_hup_signal()
{
    extern  jmp_buf   got_hup;

    if(Pmaster){
	signal(SIGHUP, (SIGTYPE *)do_hup_signal);
	longjmp(got_hup, 1);
    }
    else{
	/*
	 * if we've been interrupted and the buffer is changed,
	 * save it...
	 */
	if(anycb() == TRUE){			/* time to save */
	    if(curbp->b_fname[0] == '\0'){	/* name it */
		strcpy(curbp->b_fname, "pico.save");
	    }
	    else{
		strcat(curbp->b_fname, ".save");
	    }
	    writeout(curbp->b_fname);
	}
	vttidy();
	exit(1);
    }
}


char okinfname[32] = {
      0,    0, 			/* ^@ - ^G, ^H - ^O  */
      0,    0,			/* ^P - ^W, ^X - ^_  */
      0,    0x17,		/* SP - ' ,  ( - /   */
      0xff, 0xc0,		/*  0 - 7 ,  8 - ?   */
      0x7f, 0xff,		/*  @ - G ,  H - O   */
      0xff, 0xe1,		/*  P - W ,  X - _   */
      0x7f, 0xff,		/*  ` - g ,  h - o   */
      0xff, 0xe2,		/*  p - w ,  x - DEL */
      0,    0, 			/*  > DEL   */
      0,    0,			/*  > DEL   */
      0,    0, 			/*  > DEL   */
      0,    0, 			/*  > DEL   */
      0,    0 			/*  > DEL   */
};


/*
 * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
 */
fallowc(c)
char c;
{
    return(okinfname[c>>3] & 0x80>>(c&7));
}



/*
 * fexist - returns TRUE if the file exists, FALSE otherwise
 */
fexist(file, m, l)
char *file;
char *m;					/* files mode: r, w or rw */
long *l;
{
    struct stat	sbuf;

    if(l)
      *l = 0L;

    if(stat(file, &sbuf) < 0){
	switch(errno){
	  case ENOENT :				/* File not found */
	    return(FIOFNF);
	    break;
	  case ENAMETOOLONG :			/* Name is too long */
	    return(FIOLNG);
	    break;
	  default:				/* Some other error */
	    return(FIOERR);
	    break;
	}
    }

    if(l)
      *l = sbuf.st_size;

    if(sbuf.st_mode & S_IFDIR)
      return(FIODIR);

    if(m[0] == 'r')				/* read access? */
      return(((S_IREAD | 044) & sbuf.st_mode) ? FIOSUC : FIONRD);
    else if(m[0] == 'w')			/* write access? */
      return(((S_IWRITE | 022) & sbuf.st_mode) ? FIOSUC : FIONWT);
    else if(m[0] == 'x')			/* execute access? */
      return(((S_IEXEC | 011) & sbuf.st_mode) ? FIOSUC : FIONEX);
    return(FIOERR);				/* what? */
}


/*
 * isdir - returns true if fn is a readable directory, false otherwise
 *         silent on errors (we'll let someone else notice the problem;)).
 */
isdir(fn, l)
char *fn;
long *l;
{
    struct stat sbuf;

    if(l)
      *l = 0;

    if(stat(fn, &sbuf) < 0)
      return(0);

    if(l)
      *l = sbuf.st_size;
    return(sbuf.st_mode & S_IFDIR);
}


#if	defined(nxt) || defined(dyn)
/*
 * getcwd - NeXT uses getwd()
 */
char *getcwd(pth)
char *pth;
{
    extern char *getwd();

    return(getwd(pth));
}
#endif


/*
 * errstr - return system error string corresponding to given errno
 *          Note: strerror() is not provided on all systems, so it's 
 *          done here once and for all.
 */
char *errstr(err)
int err;
{
    extern char *sys_errlist[];
    extern int  sys_nerr;

    return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
}





/*
 * getfnames - return all file names in the given directory in a single 
 *             malloc'd string.  n contains the number of names
 */
char *getfnames(dn, n)
char *dn;
int  *n;
{
    int status;
    long l;
    char *names, *np, *p;
    struct stat sbuf;
    DIR *dirp;						/* opened directory */
#if	defined(POSIX)
    struct dirent *dp;
#else
    struct direct *dp;
#endif
    extern char *sys_errlist[];

    *n = 0;

    if(stat(dn, &sbuf) < 0){
	switch(errno){
	  case ENOENT :				/* File not found */
	    emlwrite("\007File not found: \"%s\"", dn);
	    break;
	  case ENAMETOOLONG :			/* Name is too long */
	    emlwrite("\007File name too long: \"%s\"", dn);
	    break;
	  default:				/* Some other error */
	    emlwrite("\007Error getting file info: \"%s\"", dn);
	    break;
	}
	return(NULL);
    } 
    else{
	l = sbuf.st_size;
	if(!(sbuf.st_mode & S_IFDIR)){
	    emlwrite("\007Not a directory: \"%s\"", dn);
	    return(NULL);
	}
    }

    if((names=(char *)malloc(sizeof(char)*l)) == NULL){
	emlwrite("\007Can't malloc space for file names");
	return(NULL);
    }

    errno = 0;
    if((dirp=opendir(dn)) == NULL){
	sprintf(s,"\007Can't open \"%s\": %s", dn, sys_errlist[errno]);
	emlwrite(s);
	free((char *)names);
	return(NULL);
    }

    np = names;
    while((dp = readdir(dirp)) != NULL){
	(*n)++;
	p = dp->d_name;
	while((*np++ = *p++) != '\0')
	  ;
    }

    closedir(dirp);					/* shut down */
    return(names);
}


/*
 * fioperr - given the error number and file name, display error
 */
fioperr(e, f)
int  e;
char *f;
{
    switch(e){
      case FIOFNF:				/* File not found */
	emlwrite("\007File \"%s\" not found", f);
	break;
      case FIOEOF:				/* end of file */
	emlwrite("\007End of file \"%s\" reached", f);
	break;
      case FIOLNG:				/* name too long */
	emlwrite("\007File name \"%s\" too long", f);
	break;
      case FIODIR:				/* file is a directory */
	emlwrite("\007File \"%s\" is a directory", f);
	break;
      case FIONWT:
	emlwrite("\007Write permission denied: %s", f);
	break;
      case FIONRD:
	emlwrite("\007Read permission denied: %s", f);
	break;
      case FIONEX:
	emlwrite("\007Execute permission denied: %s", f);
	break;
      default:
	emlwrite("\007File I/O error: %s", f);
    }
}



/*
 * pfnexpand - pico's function to expand the given file name if there is 
 *	       a leading '~'
 */
char *pfnexpand(fn, len)
char *fn;
int  len;
{
    struct passwd *pw;
    struct passwd *getpwnam(), *getpwuid();
    register char *x, *y, *z;
    char name[20];
    
    if(*fn == '~') {
        for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
        *y = '\0';
        if(x == fn + 1) 
          pw = getpwuid(getuid());
        else
          pw = getpwnam(name);
        if(pw == NULL)
          return(NULL);
        if(strlen(pw->pw_dir) + strlen(fn) > len) {
            return(NULL);
        }
	/* make room for expanded path */
	for(z=x+strlen(x),y=fn+strlen(x)+strlen(pw->pw_dir);
	    z >= x;
	    *y-- = *z--);
	/* and insert the expanded address */
	for(x=fn,y=pw->pw_dir; *y != '\0'; *x++ = *y++);
    }
    return(fn);
}



/*
 * fixpath - make the given pathname into an absolute path
 */
fixpath(name, len)
char *name;
int  len;
{
    register char *shft;

    if(*name != '/'){				/* filenames relative to ~ */
	if(Pmaster && (*name != '~' && strlen(name)+2 <= len)){

	    for(shft = strchr(name, '\0'); shft >= name; shft--)
	      shft[2] = *shft;

	    name[0] = '~';
	    name[1] = '/';
	}

	pfnexpand(name, len);
    }
}


/*
 * tmpname - return a temporary file name in the given buffer
 */
tmpname(name)
char *name;
{
    sprintf(name, "/tmp/pico.%d", getpid());	/* tmp file name */
}


/*
 * Take a file name, and from it
 * fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system.
 * I suppose that this information could be put in
 * a better place than a line of code.
 */
makename(bname, fname)
char    bname[];
char    fname[];
{
        register char   *cp1;
        register char   *cp2;

        cp1 = &fname[0];
        while (*cp1 != 0)
                ++cp1;

        while (cp1!=&fname[0] && cp1[-1]!='/')
                --cp1;
        cp2 = &bname[0];
        while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
                *cp2++ = *cp1++;
        *cp2 = 0;
}



/*
 * Open a file for writing. Return TRUE if all is well, and FALSE on error
 * (cannot create).
 */
ffwopen(fn)
char    *fn;
{
        extern FILE *ffp;

        if ((ffp=fopen(fn, "w")) == NULL) {
                emlwrite("Cannot open file for writing");
                return (FIOERR);
        }
        return (FIOSUC);
}


/*
 * Close a file. Should look at the status in all systems.
 */
ffclose()
{
        extern FILE *ffp;

        if (fclose(ffp) != FALSE) {
                emlwrite("Error closing file");
                return(FIOERR);
        }
        return(FIOSUC);
}


/*
 * P_open - run the given command in a sub-shell returning a file pointer
 *	    from which to read the output
 *
 * note:
 *	For OS's other than unix, you will have to rewrite this function.
 *	Hopefully it'll be easy to exec the command into a temporary file, 
 *	and return a file pointer to that opened file or something.
 */
FILE *P_open(s)
char *s;
{
    return(popen(s, "r"));
}



/*
 * P_close - close the given descriptor
 *
 */
P_close(fp)
FILE *fp;
{
    return(pclose(fp));
}



/*
 * worthit - generic sort of test to roughly gage usefulness of using 
 *           optimized scrolling.
 *
 * note:
 *	returns the line on the screen, l, that the dot is currently on
 */
worthit(l)
int *l;
{
    int i;			/* l is current line */
    unsigned below;		/* below is avg # of ch/line under . */

    *l = doton(&i, &below);
    below = (i > 0) ? below/(unsigned)i : 0;

    return(below > 3);
}




/*
 * pico_new_mail - just checks mtime and atime of mail file and notifies user 
 *	           if it's possible that they have new mail.
 */
pico_new_mail()
{
    int ret = 0;
    static time_t lastchk = 0;
    struct stat sbuf;
    char   inbox[256];

    sprintf(inbox,"/usr/spool/mail/%s", getlogin());
    if(stat(inbox, &sbuf) == 0){
	ret = sbuf.st_atime <= sbuf.st_mtime &&
	  (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
	lastchk = sbuf.st_mtime;
	return(ret);
    }
    else
      return(ret);
}



/*
 * time_to_check - checks the current time against the last time called 
 *                 and returns true if the elapsed time is > timeout
 */
time_to_check()
{
    static time_t lasttime = 0L;

    if(!timeout)
      return(FALSE);

    if(time((long *) 0) - lasttime > (time_t)timeout){
	lasttime = time((long *) 0);
	return(TRUE);
    }
    else
      return(FALSE);
}


/*
 * sstrcasecmp - compare two pointers to strings case independently
 */
sstrcasecmp(s1, s2)
char **s1, **s2;
{
    register char *a, *b;

    a = *s1;
    b = *s2;
    while(toupper(*a) == toupper(*b++))
	if(*a++ == '\0')
	  return(0);

    return(toupper(*a) - toupper(*--b));
}


#ifdef	TIOCGWINSZ
/*
 * winch_handler - handle window change signal
 */
SIGTYPE winch_handler()
{
    struct winsize win;
    extern int resize_pico();

    signal(SIGWINCH, (SIGTYPE *)winch_handler);

    if (ioctl(0, TIOCGWINSZ, &win) == 0) {
	if (win.ws_col && win.ws_row)
	  resize_pico(win.ws_row - 1, win.ws_col);
    }
}
#endif	/* TIOCGWINSZ */
