int timeo = 0;
int (*pcollator)();

#if	defined(sv3) || defined(ct)
/*
 * Windowing structure to support JWINSIZE/TIOCSWINSZ/TIOCGWINSZ 
 */
#define ENAMETOOLONG	78

struct winsize {
	unsigned short ws_row;       /* rows, in characters*/
	unsigned short ws_col;       /* columns, in character */
	unsigned short ws_xpixel;    /* horizontal size, pixels */
	unsigned short ws_ypixel;    /* vertical size, pixels */
};
#endif

#ifdef	SIGCHLD
static jmp_buf pico_child_state;
static short   pico_child_jmp_ok, pico_child_done;
#endif

#ifdef	MOUSE
static int mexist = 0;		/* is the mouse driver installed? */
static unsigned mnoop;
#endif

struct color_table {
    char *name;
    int   namelen;
    char *rgb;
    int   val;
};

static unsigned color_options;
#define	ANSI8_COLOR()	(color_options & COLOR_ANSI8_OPT)
#define	ANSI16_COLOR()	(color_options & COLOR_ANSI16_OPT)
#define	ANSI_COLOR()	(color_options & (COLOR_ANSI8_OPT | COLOR_ANSI16_OPT))
#define	END_PSEUDO_REVERSE	"EndInverse"
static COLOR_PAIR *the_rev_color;
static COLOR_PAIR *color_blasted_by_attrs;
static int pinvstate;	/* physical state of inverse (standout) attribute */
static int pboldstate;	/* physical state of bold attribute */
static int pulstate;	/* physical state of underline attribute */
static int rev_color_state;

static int boldstate;	/* should-be state of bold attribute */
static int ulstate;	/* should-be state of underline attribute */
static int invstate;	/* should-be state of Inverse, which could be a color
			   change or an actual setinverse */
#define A_UNKNOWN	-1


void	 kpinsert PROTO((char *, int, int));
void	 bail PROTO(());
void     flip_rev_color PROTO((int));
void     flip_bold PROTO((int));
void     flip_inv PROTO((int));
void     flip_ul PROTO((int));
void     reset_attr_state PROTO((void));
SigType	 do_hup_signal SIG_PROTO((int));
SigType	 rtfrmshell SIG_PROTO((int));
int	 pathcat PROTO((char *, char **, char *));
#ifdef	RESIZING
SigType	winch_handler SIG_PROTO((int));
#endif
#ifdef	SIGCHLD
SigType	child_handler SIG_PROTO((int));
#endif

extern char    *tgoto();
#if defined(USE_TERMINFO)
extern char    *tparm();
#endif
static void     putpad PROTO((char *));
static void	tinitcolor PROTO((void));
static int	tfgcolor PROTO((int));
static int	tbgcolor PROTO((int));
static struct color_table *init_color_table PROTO((void));
static void	free_color_table PROTO((struct color_table **));

extern char   *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
extern int	_colors;
static int     _color_inited, _using_color;
static char   *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
static char   *_last_fg_color, *_last_bg_color;
static int     _force_fg_color_change;
static int     _force_bg_color_change;

static struct color_table *color_tbl;
static int dummy;


/*
 * for alt_editor arg[] building
 */
#define	MAXARGS	10

/*
 * ttopen - this function is called once to set up the terminal device 
 *          streams.  if called as pine composer, don't mess with
 *          tty modes, but set signal handlers.
 */
ttopen()
{
    if(Pmaster == NULL){
	Raw(1);
#ifdef	MOUSE
	if(gmode & MDMOUSE)
	  init_mouse();
#endif	/* MOUSE */
    }

    if(!Pmaster || (gmode ^ MDEXTFB))
      picosigs();

    return(1);
}


/*
 * ttresize - recompute the screen dimensions if necessary, and then
 *	      adjust pico's internal buffers accordingly.
 */
void
ttresize ()
{
    int row = -1, col = -1;

    ttgetwinsz(&row, &col);
    resize_pico(row, col);
}


/*
 * picosigs - Install any handlers for the signals we're interested
 *	      in catching.
 */
picosigs()
{
    signal(SIGHUP,  do_hup_signal);	/* deal with SIGHUP */
    signal(SIGTERM, do_hup_signal);	/* deal with SIGTERM */
#ifdef	SIGTSTP
    signal(SIGTSTP, SIG_DFL);
#endif
#ifdef	RESIZING
    signal(SIGWINCH, winch_handler); /* window size changes */
#endif
}


/*
 * ttclose - this function gets called just before we go back home to 
 *           the command interpreter.  If called as pine composer, don't
 *           worry about modes, but set signals to default, pine will 
 *           rewire things as needed.
 */
ttclose()
{
    if(Pmaster){
	signal(SIGHUP, SIG_DFL);
#ifdef  SIGCONT
	signal(SIGCONT, SIG_DFL);
#endif
#ifdef  RESIZING
	signal(SIGWINCH, SIG_DFL);
#endif
    }
    else{
	Raw(0);
#ifdef  MOUSE
	end_mouse();
#endif
    }

    return(1);
}


/*
 * ttgetwinsz - set global row and column values (if we can get them)
 *		and return.
 */
ttgetwinsz(row, col)
    int *row, *col;
{
    extern int _tlines, _tcolumns;

    if(*row < 0)
      *row = (_tlines > 0) ? _tlines - 1 : NROW - 1;
    if(*col <= 0)
      *col = (_tcolumns > 0) ? _tcolumns : NCOL;
#ifdef RESIZING
    {
	struct winsize win;

	if (ioctl(0, TIOCGWINSZ, &win) == 0) {	/* set to anything useful.. */
	    if(win.ws_row)			/* ... the tty drivers says */
	      *row = win.ws_row - 1;

	    if(win.ws_col)
	      *col = win.ws_col;
	}

	signal(SIGWINCH, winch_handler);	/* window size changes */
    }
#endif
}


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


/*
 * ttflush - 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()
{
    return(fflush(stdout));
}


/*
 * ttgetc - Read a character from the terminal, performing no editing 
 *          and doing no echo at all.
 *
 * Args: return_on_intr     -- Function to get a single character from stdin,
 *             recorder     -- If non-NULL, function used to record keystroke.
 *             bail_handler -- Function used to bail out on read error.
 *
 *  Returns: The character read from stdin.
 *           Return_on_intr is returned if read is interrupted.
 *           If read error, BAIL_OUT is returned unless bail_handler is
 *             non-NULL, in which case it is called (and usually it exits).
 *
 *      If recorder is non-null, it is used to record the keystroke.
 */
int
ttgetc(return_on_intr, recorder, bail_handler)
    int  return_on_intr;
    int  (*recorder) PROTO((int));
    void (*bail_handler)();
{
    int c;

    switch(c = read_one_char()){
      case READ_INTR:
	return(return_on_intr);

      case BAIL_OUT:
	if(bail_handler)
	  (*bail_handler)();
	else
	  return(BAIL_OUT);

      default:
        return(recorder ? (*recorder)(c) : c);
    }
}

/*
 * Simple version of ttgetc with simple error handling
 *
 *      Args:  recorder     -- If non-NULL, function used to record keystroke.
 *             bail_handler -- Function used to bail out on read error.
 *
 *  Returns: The character read from stdin.
 *           If read error, BAIL_OUT is returned unless bail_handler is
 *             non-NULL, in which case it is called (and usually it exits).
 *
 *      If recorder is non-null, it is used to record the keystroke.
 *      Retries if interrupted.
 */
int
simple_ttgetc(recorder, bail_handler)
    int  (*recorder) PROTO((int));
    void (*bail_handler)();
{
    int		  res;
    unsigned char c;

    while((res = read(STDIN_FD, &c, 1)) <= 0)
      if(!(res < 0 && errno == EINTR))
	(*bail_handler)();

    return(recorder ? (*recorder)((int)c) : (int)c);
}

void
bail()
{
    sleep(30);				/* see if os receives SIGHUP */
    kill(getpid(), SIGHUP);			/* eof or bad error */
}


#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


/*
 * ReadyForKey - return true if there's no timeout or we're told input
 *		 is available...
 */
ReadyForKey(timeout)
    int timeout;
{
    switch(input_ready(timeout)){
      case READY_TO_READ:
	return(1);
	break;

      case NO_OP_COMMAND:
      case NO_OP_IDLE:
      case READ_INTR:
	return(0);

      case BAIL_OUT:
      case PANIC_NOW:
	emlwrite("\007Problem reading from keyboard!", NULL);
	kill(getpid(), SIGHUP);	/* Bomb out (saving our work)! */
	/* no return */
    }
}


/*
 * GetKey - 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    ch, status, cc;

    if(!ReadyForKey(timeo))
      return(NODATA);

    switch(status = kbseq(simple_ttgetc, NULL, bail, &ch)){
      case 0: 	/* regular character */
	break;

      case KEY_DOUBLE_ESC:
	/*
	 * Special hack to get around comm devices eating control characters.
	 */
	if(!ReadyForKey(5))
	  return(BADESC);		/* user typed ESC ESC, then stopped */
	else
	  ch = (*term.t_getchar)(NODATA, NULL, bail);

	ch &= 0x7f;
	if(isdigit((unsigned char)ch)){
	    int n = 0, i = ch - '0';

	    if(i < 0 || i > 2)
	      return(BADESC);		/* bogus literal char value */

	    while(n++ < 2){
		if(!ReadyForKey(5)
		   || (!isdigit((unsigned char) (ch =
				   (*term.t_getchar)(NODATA, NULL, bail)))
		       || (n == 1 && i == 2 && ch > '5')
		       || (n == 2 && i == 25 && ch > '5'))){
		    return(BADESC);
		}

		i = (i * 10) + (ch - '0');
	    }

	    ch = i;
	}
	else{
	    if(islower((unsigned char)ch))	/* canonicalize if alpha */
	      ch = toupper((unsigned char)ch);

	    return((isalpha((unsigned char)ch) || ch == '@'
		    || (ch >= '[' && ch <= '_'))
		    ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
	}

	break;

#ifdef MOUSE
      case KEY_XTERM_MOUSE:
	{
	    /*
	     * Special hack to get mouse events from an xterm.
	     * Get the details, then pass it past the keymenu event
	     * handler, and then to the installed handler if there
	     * is one...
	     */
	    static int down = 0;
	    int        x, y, button;
	    unsigned   cmd;

	    button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;

	    x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
	    y = (*term.t_getchar)(NODATA, NULL, bail) - '!';

	    if(button == 0){
		down = 1;
		if(checkmouse(&cmd, 1, x, y))
		  return((int)cmd);
	    }
	    else if(down && button == 3){
		down = 0;
		if(checkmouse(&cmd, 0, x, y))
		  return((int)cmd);
	    }

	    return(NODATA);
	}

	break;
#endif /* MOUSE */

      case  KEY_UP		:
      case  KEY_DOWN		:
      case  KEY_RIGHT		:
      case  KEY_LEFT		:
      case  KEY_PGUP		:
      case  KEY_PGDN		:
      case  KEY_HOME		:
      case  KEY_END		:
      case  KEY_DEL		:
      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 KEY_SWALLOW_Z:
	status = BADESC;
      case KEY_SWAL_UP:
      case KEY_SWAL_DOWN:
      case KEY_SWAL_LEFT:
      case KEY_SWAL_RIGHT:
	do
	  if(!ReadyForKey(2)){
	      status = BADESC;
	      break;
	  }
	while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));

	return((status == BADESC)
		? status
		: status - (KEY_SWAL_UP - KEY_UP));
	break;

      case KEY_KERMIT:
	do{
	    cc = ch;
	    if(!ReadyForKey(2))
	      return(BADESC);
	    else
	      ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f;
	}while(cc != '\033' && ch != '\\');

	ch = NODATA;
	break;

      case BADESC:
	(*term.t_beep)();
	return(status);

      default:				/* punt the whole thing	*/
	(*term.t_beep)();
	break;
    }

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

    return(ch);
}



/* 
 * kbseq - looks at an escape sequence coming from the keyboard and 
 *         compares it to a trie of known keyboard escape sequences, and
 *         returns the function bound to the escape sequence.
 * 
 *     Args: getcfunc     -- Function to get a single character from stdin,
 *                           called with the next two arguments to this
 *                           function as its arguments.
 *           recorder     -- If non-NULL, function used to record keystroke.
 *           bail_handler -- Function used to bail out on read error.
 *           c            -- Pointer to returned character.
 *
 *  Returns: BADESC
 *           The escaped function.
 *           0 if a regular char with char stuffed in location c.
 */
kbseq(getcfunc, recorder, bail_handler, c)
    int  (*getcfunc)();
    int  (*recorder)();
    void (*bail_handler)();
    int   *c;
{
    register char     b;
    register int      first = 1;
    register KBESC_T *current;

    current = kbesc;
    if(current == NULL)				/* bag it */
      return(BADESC);

    while(1){
	b = *c = (*getcfunc)(recorder, bail_handler);

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

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

	first = 0;
    }
}


#define	newnode()	(KBESC_T *)malloc(sizeof(KBESC_T))
/*
 * kpinsert - insert a keystroke escape sequence into the global search
 *	      structure.
 */
void
kpinsert(kstr, kval, termcap_wins)
    char     *kstr;
    int       kval;
    int       termcap_wins;
{
    register	char	*buf;
    register	KBESC_T *temp;
    register	KBESC_T *trail;

    if(kstr == NULL)
      return;

    /*
     * Don't allow escape sequences that don't start with ESC unless
     * termcap_wins.  This is to protect against mistakes in termcap files.
     */
    if(!termcap_wins && *kstr != '\033')
      return;

    temp = trail = kbesc;
    buf = kstr;

    for(;;){
	if(temp == NULL){
	    temp = newnode();
	    temp->value = *buf;
	    temp->func = 0;
	    temp->left = NULL;
	    temp->down = NULL;
	    if(kbesc == NULL)
	      kbesc = temp;
	    else
	      trail->down = temp;
	}
	else{				/* first entry */
	    while((temp != NULL) && (temp->value != *buf)){
		trail = temp;
		temp = temp->left;
	    }

	    if(temp == NULL){   /* add new val */
		temp = newnode();
		temp->value = *buf;
		temp->func = 0;
		temp->left = NULL;
		temp->down = NULL;
		trail->left = temp;
	    }
	}

	if(*(++buf) == '\0')
	  break;
	else{
	    /*
	     * Ignore attempt to overwrite shorter existing escape sequence.
	     * That means that sequences with higher priority should be
	     * set up first, so if we want termcap sequences to override
	     * hardwired sequences, put the kpinsert calls for the
	     * termcap sequences first.  (That's what you get if you define
	     * TERMCAP_WINS.)
	     */
	    if(temp->func != 0)
	      return;

	    trail = temp;
	    temp = temp->down;
	}
    }
    
    /*
     * Ignore attempt to overwrite longer sequences we are a prefix
     * of (down != NULL) and exact same sequence (func != 0).
     */
    if(temp != NULL && temp->down == NULL && temp->func == 0)
      temp->func = kval;
}



/*
 * kbdestroy() - kills the key pad function key search tree
 *		 and frees all lines associated with it
 *
 *               Should be called with arg kbesc, the top of the tree.
 */
void
kbdestroy(kb)
    KBESC_T *kb;
{
    if(kb){
	kbdestroy(kb->left);
	kbdestroy(kb->down);
	free((char *)kb);
	kb = NULL;
    }
}


/*
 *     Start or end bold mode
 *
 * Result: escape sequence to go into or out of reverse color is output
 *
 *     (This is only called when there is a reverse color.)
 *
 * Arg   state = ON   set bold
 *               OFF  set normal
 */
void
flip_rev_color(state)
    int state;
{
    if((rev_color_state = state) == TRUE)
      (void)pico_set_colorp(the_rev_color, PSC_NONE);
    else
      pico_set_normal_color();
}


/*
 *     Start or end bold mode
 *
 * Result: escape sequence to go into or out of bold is output
 *
 * Arg   state = ON   set bold
 *               OFF  set normal
 */
void
flip_bold(state)
    int state;
{
    extern char *_setbold, *_clearallattr;

    if((pboldstate = state) == TRUE){
	if(_setbold != NULL)
	  putpad(_setbold);
    }
    else{
	if(_clearallattr != NULL){
	    if(!color_blasted_by_attrs)
	      color_blasted_by_attrs = pico_get_cur_color();

	    _force_fg_color_change = _force_bg_color_change = 1;
	    putpad(_clearallattr);
	    pinvstate = state;
	    pulstate = state;
	    rev_color_state = state;
	}
    }
}


/*
 *     Start or end inverse mode
 *
 * Result: escape sequence to go into or out of inverse is output
 *
 * Arg   state = ON   set inverse
 *               OFF  set normal
 */
void
flip_inv(state)
    int state;
{
    extern char *_setinverse, *_clearinverse;

    if((pinvstate = state) == TRUE){
	if(_setinverse != NULL)
	  putpad(_setinverse);
    }
    else{
	/*
	 * Unfortunately, some termcap entries configure end standout to
	 * be clear all attributes.
	 */
	if(_clearinverse != NULL){
	    if(!color_blasted_by_attrs)
	      color_blasted_by_attrs = pico_get_cur_color();

	    _force_fg_color_change = _force_bg_color_change = 1;
	    putpad(_clearinverse);
	    pboldstate = (pboldstate == FALSE) ?  pboldstate : A_UNKNOWN;
	    pulstate = (pulstate == FALSE) ?  pulstate : A_UNKNOWN;
	    rev_color_state = A_UNKNOWN;
	}
    }
}


/*
 *     Start or end underline mode
 *
 * Result: escape sequence to go into or out of underline is output
 *
 * Arg   state = ON   set underline
 *               OFF  set normal
 */
void
flip_ul(state)
    int state;
{
    extern char *_setunderline, *_clearunderline;

    if((pulstate = state) == TRUE){
	if(_setunderline != NULL)
	  putpad(_setunderline);
    }
    else{
	/*
	 * Unfortunately, some termcap entries configure end underline to
	 * be clear all attributes.
	 */
	if(_clearunderline != NULL){
	    if(!color_blasted_by_attrs)
	      color_blasted_by_attrs = pico_get_cur_color();

	    _force_fg_color_change = _force_bg_color_change = 1;
	    putpad(_clearunderline);
	    pboldstate = (pboldstate == FALSE) ?  pboldstate : A_UNKNOWN;
	    pinvstate = (pinvstate == FALSE) ?  pinvstate : A_UNKNOWN;
	    rev_color_state = A_UNKNOWN;
	}
    }
}

void
StartInverse()
{
    invstate = TRUE;
    reset_attr_state();
}


void
EndInverse()
{
    invstate = FALSE;
    reset_attr_state();
}


int
InverseState()
{
    return(invstate);
}

int
StartBold()
{
    extern char *_setbold;

    boldstate = TRUE;
    reset_attr_state();
    return(_setbold != NULL);
}

void
EndBold()
{
    boldstate = FALSE;
    reset_attr_state();
}

void
StartUnderline()
{
    ulstate = TRUE;
    reset_attr_state();
}

void
EndUnderline()
{
    ulstate = FALSE;
    reset_attr_state();
}

void
reset_attr_state()
{
    /*
     * If we have to turn some attributes off, do that first since that
     * may turn off other attributes as a side effect.
     */
    if(boldstate == FALSE && pboldstate != boldstate)
      flip_bold(boldstate);

    if(ulstate == FALSE && pulstate != ulstate)
      flip_ul(ulstate);

    if(invstate == FALSE){
	if(pico_get_rev_color()){
	    if(rev_color_state != invstate)
	      flip_rev_color(invstate);
	}
	else{
	    if(pinvstate != invstate)
	      flip_inv(invstate);
	}
    }
    
    /*
     * Now turn everything on that needs turning on.
     */
    if(boldstate == TRUE && pboldstate != boldstate)
      flip_bold(boldstate);

    if(ulstate == TRUE && pulstate != ulstate)
      flip_ul(ulstate);

    if(invstate == TRUE){
	if(pico_get_rev_color()){
	    if(rev_color_state != invstate)
	      flip_rev_color(invstate);
	}
	else{
	    if(pinvstate != invstate)
	      flip_inv(invstate);
	}
    }

    if(color_blasted_by_attrs){
	(void)pico_set_colorp(color_blasted_by_attrs, PSC_NONE);
	free_color_pair(&color_blasted_by_attrs);
    }

}


static void
tinitcolor()
{
    if(_color_inited)
      return;

    if(ANSI_COLOR() || (_colors > 0 && ((_setaf && _setab) || (_setf && _setb)
/**** not sure how to do this yet
       || _scp
****/
	))){
	_color_inited = 1;
	color_tbl = init_color_table();

	if(ANSI_COLOR())
	  putpad("\033[39;49m");
	else{
	    if(_op)
	      putpad(_op);
	    if(_oc)
	      putpad(_oc);
	}
    }
}


#if defined(USE_TERMCAP)
/*
 * Treading on thin ice here. Tgoto wasn't designed for this. It takes
 * arguments column and row. We only use one of them, so we put it in
 * the row argument. The 0 is just a placeholder.
 */
#define tparm(s, c) tgoto(s, 0, c)
#endif

static int
tfgcolor(color)
    int color;
{
    if(!_color_inited)
      tinitcolor();

    if(!_color_inited)
      return(-1);
    
    if((ANSI8_COLOR()  && (color < 0 || color >= 8)) ||
       (ANSI16_COLOR() && (color < 0 || color >= 16)) ||
       (!ANSI_COLOR()  && (color < 0 || color >= _colors)))
      return(-1);

    if(ANSI_COLOR()){
	char buf[10];

	if(color < 8)
	  sprintf(buf, "\033[3%cm", color + '0');
	else
	  sprintf(buf, "\033[9%cm", (color-8) + '0');
	
	putpad(buf);
    }
    else if(_setaf)
      putpad(tparm(_setaf, color));
    else if(_setf)
      putpad(tparm(_setf, color));
    else if(_scp){
	/* set color pair method */
    }

    return(0);
}


static int
tbgcolor(color)
    int color;
{
    if(!_color_inited)
      tinitcolor();

    if(!_color_inited)
      return(-1);
    
    if((ANSI8_COLOR()  && (color < 0 || color >= 8)) ||
       (ANSI16_COLOR() && (color < 0 || color >= 16)) ||
       (!ANSI_COLOR()  && (color < 0 || color >= _colors)))
      return(-1);

    if(ANSI_COLOR()){
	char buf[10];

	if(color < 8)
	  sprintf(buf, "\033[4%cm",  color + '0');
	else
	  sprintf(buf, "\033[10%cm", (color-8) + '0');
	
	putpad(buf);
    }
    else if(_setab)
      putpad(tparm(_setab, color));
    else if(_setb)
      putpad(tparm(_setb, color));
    else if(_scp){
	/* set color pair method */
    }

    return(0);
}



/*
 * We're not actually using the RGB value other than as a string which
 * maps into the color.
 * In fact, on some systems color 1 and color 4 are swapped, and color 3
 * and color 6 are swapped. So don't believe the RGB values.
 * Still, it feels right to have them be the canonical values, so we do that.
 */
static struct color_table *
init_color_table()
{
    struct color_table *ct = NULL, *t;
    int                 i, size, count;

    count = pico_count_in_color_table();

    /*
     * Special case: If table is of size 8 we use a table of size 16 instead
     * and map the 2nd eight into the first 8 so that both 8 and 16 color
     * terminals can be used with the same pinerc and colors in the 2nd
     * 8 may be useful. We could make this more general and always map
     * colors larger than our current # of colors by mapping it modulo
     * the color table size. The only problem with this is that it would
     * take a boatload of programming to convert what we have now into that,
     * and it is highly likely that no one would ever use anything other
     * than the 8/16 case. So we'll stick with that special case for now.
     *
     * The other place where code is modified for this is in other.c's
     * add_color_setting_disp(). The arguments to new_color_line have a
     * special case for (count == 8).
     */
    if(count == 8)
      size = 16;
    else
      size = count;

    if(size > 0 || size < 256){
	ct = (struct color_table *)malloc((size+1)*sizeof(struct color_table));
	if(ct)
	  memset(ct, 0, (size+1) * sizeof(struct color_table));

	for(i = 0, t = ct; t && i < size; i++, t++){
	    t->val = i % count;
	    t->namelen = strlen(colorx(i));
	    t->name = (char *)malloc((t->namelen+1) * sizeof(char));
	    if(t->name)
	      strcpy(t->name, colorx(i));

	    t->rgb = (char *)malloc((RGBLEN+1) * sizeof(char));
	    if(t->rgb){
	      switch(i){
	        case COL_BLACK:
		  strcpy(t->rgb, "  0,  0,  0");
		  break;
	        case COL_RED:
		  strcpy(t->rgb, "255,  0,  0");
		  break;
	        case COL_GREEN:
		  strcpy(t->rgb, "  0,255,  0");
		  break;
	        case COL_YELLOW:
		  strcpy(t->rgb, "255,255,  0");
		  break;
	        case COL_BLUE:
		  strcpy(t->rgb, "  0,  0,255");
		  break;
	        case COL_MAGENTA:
		  strcpy(t->rgb, "255,  0,255");
		  break;
	        case COL_CYAN:
		  strcpy(t->rgb, "  0,255,255");
		  break;
	        case COL_WHITE:
		  strcpy(t->rgb, "255,255,255");
		  break;
	        default:
		  /*
		   * Since these are just unique strings, they don't actually
		   * even have to be rgb values. Make them rgb-like in case
		   * something checks.
		   * We are using the fact that all rgb values start with
		   * a digit or space and color names don't in the lookup
		   * routines below.
		   */
		  sprintf(t->rgb, "%d,%d,%d", 256+i, 256+i, 256+i);
		  break;
	      }
	    }
	}
    }

    return(ct);
}


static void
free_color_table(ctbl)
    struct color_table **ctbl;
{
    int i;
    struct color_table *t;

    if(ctbl && *ctbl){
	for(i = 0, t = *ctbl; t->name; t++){
	    if(t->name)
	      free(t->name);
	    if(t->rgb)
	      free(t->rgb);
	}
	
	free(*ctbl);
	*ctbl = NULL;
    }
}


int
pico_count_in_color_table()
{
    return(ANSI8_COLOR() ? 8 : ANSI16_COLOR() ? 16 : _colors);
}


void
pico_nfcolor(s)
    char *s;
{
    if(_nfcolor){
	free(_nfcolor);
	_nfcolor = NULL;
    }

    if(s){
	_nfcolor = (char *)malloc(strlen(s)+1);
	if(_nfcolor)
	  strcpy(_nfcolor, s);
    }
}


void
pico_nbcolor(s)
    char *s;
{
    if(_nbcolor){
	free(_nbcolor);
	_nbcolor = NULL;
    }

    if(s){
	_nbcolor = (char *)malloc(strlen(s)+1);
	if(_nbcolor)
	  strcpy(_nbcolor, s);
    }
}

void
pico_rfcolor(s)
    char *s;
{
    if(_rfcolor){
	free(_rfcolor);
	_rfcolor = NULL;
    }

    if(s){
	_rfcolor = (char *)malloc(strlen(s)+1);
	if(_rfcolor)
	  strcpy(_rfcolor, s);
	
	if(the_rev_color)
	  strcpy(the_rev_color->fg, _rfcolor);
    }
    else if(the_rev_color)
      free_color_pair(&the_rev_color);
}

void
pico_rbcolor(s)
    char *s;
{
    if(_rbcolor){
	free(_rbcolor);
	_rbcolor = NULL;
    }

    if(s){
	_rbcolor = (char *)malloc(strlen(s)+1);
	if(_rbcolor)
	  strcpy(_rbcolor, s);
	
	if(the_rev_color)
	  strcpy(the_rev_color->bg, _rbcolor);
    }
    else if(the_rev_color)
      free_color_pair(&the_rev_color);
}

int
pico_hascolor()
{
    if(!_color_inited)
      tinitcolor();

    return(_color_inited);
}

int
pico_usingcolor()
{
    return(_using_color && pico_hascolor());
}

void
pico_toggle_color(on)
    int on;
{
    if(on){
	if(pico_hascolor())
	  _using_color = 1;
    }
    else{
	_using_color = 0;
	if(_color_inited){
	    _color_inited = 0;
	    free_color_table(&color_tbl);
	    if(ANSI_COLOR())
	      putpad("\033[39;49m");
	    else{
		if(_op)
		  putpad(_op);
		if(_oc)
		  putpad(_oc);
	    }
	}
    }
}

unsigned
pico_get_color_options()
{
    return(color_options);
}

/*
 * Absolute set of options. Caller has to OR things together and so forth.
 */
void
pico_set_color_options(flags)
    unsigned flags;
{
    color_options = flags;
}

void
pico_endcolor()
{
    pico_toggle_color(0);

    if(_nfcolor){
	free(_nfcolor);
	_nfcolor = NULL;
    }

    if(_nbcolor){
	free(_nbcolor);
	_nbcolor = NULL;
    }

    if(_rfcolor){
	free(_rfcolor);
	_rfcolor = NULL;
    }

    if(_rbcolor){
	free(_rbcolor);
	_rbcolor = NULL;
    }

    if(the_rev_color)
      free_color_pair(&the_rev_color);
}

void
pico_set_nfg_color()
{
    if(_nfcolor)
      (void)pico_set_fg_color(_nfcolor);
}

void
pico_set_nbg_color()
{
    if(_nbcolor)
      (void)pico_set_bg_color(_nbcolor);
}

void
pico_set_normal_color()
{
    if(!_nfcolor || !_nbcolor ||
       !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
	(void)pico_set_fg_color(colorx(DEFAULT_NORM_FORE));
	(void)pico_set_bg_color(colorx(DEFAULT_NORM_BACK));
    }
}

/*
 * If inverse is a color, returns a pointer to that color.
 * If not, returns NULL.
 *
 * NOTE: Don't free this!
 */
COLOR_PAIR *
pico_get_rev_color()
{
    if(pico_usingcolor() && _rfcolor && _rbcolor &&
       pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
	if(!the_rev_color)
	  the_rev_color = new_color_pair(_rfcolor, _rbcolor);
	
	return(the_rev_color);
    }
    else
      return(NULL);
}

/*
 * Just like pico_set_color except it doesn't set the color, it just
 * returns the value. Assumes def of PSC_NONE, since otherwise we always
 * succeed and don't need to call this.
 */
int
pico_is_good_colorpair(cp)
    COLOR_PAIR *cp;
{
    if(!cp || (!pico_is_good_color(cp->fg) || !pico_is_good_color(cp->bg)))
      return(FALSE);

    return(TRUE);
}


COLOR_PAIR *
pico_set_colorp(col, flags)
    COLOR_PAIR *col;
    int         flags;
{
    return(pico_set_colors(col ? col->fg : NULL, col ? col->bg : NULL, flags));
}


/*
 * Sets color to (fg,bg).
 * Flags == PSC_NONE  No alternate default if fg,bg fails.
 *       == PSC_NORM  Set it to Normal color on failure.
 *       == PSC_REV   Set it to Reverse color on failure.
 *
 * If flag PSC_RET is set, returns an allocated copy of the previous
 * color pair, otherwise returns NULL.
 */
COLOR_PAIR *
pico_set_colors(fg, bg, flags)
    char *fg, *bg;
    int   flags;
{
    int         uc;
    COLOR_PAIR *cp = NULL, *rev = NULL;

    if(flags & PSC_RET)
      cp = pico_get_cur_color();

    if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){
	EndInverse();
	if(cp)
	  free_color_pair(&cp);
    }
    else if(!((uc=pico_usingcolor()) && fg && bg &&
	      pico_set_fg_color(fg) && pico_set_bg_color(bg))){

	if(uc && flags & PSC_NORM)
	  pico_set_normal_color();
	else if(flags & PSC_REV){
	    if(rev = pico_get_rev_color()){
		pico_set_fg_color(rev->fg);	/* these will succeed */
		pico_set_bg_color(rev->bg);
	    }
	    else{
		StartInverse();
		if(cp){
		    strcpy(cp->fg, END_PSEUDO_REVERSE);
		    strcpy(cp->bg, END_PSEUDO_REVERSE);
		}
	    }
	}
    }

    return(cp);
}


int
pico_is_good_color(s)
    char *s;
{
    struct color_table *ct;

    if(!s)
      return(FALSE);

    if(!color_tbl)
      return(FALSE);

    if(!strcmp(s, END_PSEUDO_REVERSE))
      return(TRUE);
    else if(*s == ' ' || isdigit(*s)){
	/* check for rgb string instead of name */
	for(ct = color_tbl; ct->rgb; ct++)
	  if(!strncmp(ct->rgb, s, RGBLEN))
	    break;
    }
    else{
	for(ct = color_tbl; ct->name; ct++)
	  if(!struncmp(ct->name, s, ct->namelen))
	    break;
    }
    
    return(ct->name ? TRUE : FALSE);
}


/*
 * Return TRUE on success.
 */
int
pico_set_fg_color(s)
    char *s;
{
    struct color_table *ct;

    if(!s)
      return(FALSE);

    if(!color_tbl)
      return(FALSE);

    if(!strcmp(s, END_PSEUDO_REVERSE)){
	EndInverse();
	return(TRUE);
    }
    else if(*s == ' ' || isdigit(*s)){
	/* check for rgb string instead of name */
	for(ct = color_tbl; ct->rgb; ct++)
	  if(!strncmp(ct->rgb, s, RGBLEN))
	    break;
    }
    else{
	for(ct = color_tbl; ct->name; ct++)
	  if(!struncmp(ct->name, s, ct->namelen))
	    break;
    }
    
    if(ct->name){
	/* already set correctly */
	if(!_force_fg_color_change && _last_fg_color &&
	   !strcmp(_last_fg_color,ct->name))
	  return(TRUE);

	_force_fg_color_change = 0;
	if(_last_fg_color)
	  free(_last_fg_color);
	
	if(_last_fg_color = (char *)malloc(strlen(ct->name)+1))
	  strcpy(_last_fg_color, ct->name);

	tfgcolor(ct->val);
	return(TRUE);
    }
    else
      return(FALSE);
}


int
pico_set_bg_color(s)
    char *s;
{
    struct color_table *ct;

    if(!s)
      return(FALSE);

    if(!color_tbl)
      return(FALSE);

    if(!strcmp(s, END_PSEUDO_REVERSE)){
	EndInverse();
	return(TRUE);
    }
    else if(*s == ' ' || isdigit(*s)){
	/* check for rgb string instead of name */
	for(ct = color_tbl; ct->rgb; ct++)
	  if(!strncmp(ct->rgb, s, RGBLEN))
	    break;
    }
    else{
	for(ct = color_tbl; ct->name; ct++)
	  if(!struncmp(ct->name, s, ct->namelen))
	    break;
    }
    
    if(ct->name){
	/* already set correctly */
	if(!_force_bg_color_change && _last_bg_color &&
	   !strcmp(_last_bg_color,ct->name))
	  return(TRUE);

	_force_bg_color_change = 0;
	if(_last_bg_color)
	  free(_last_bg_color);
	
	if(_last_bg_color = (char *)malloc(strlen(ct->name)+1))
	  strcpy(_last_bg_color, ct->name);

	tbgcolor(ct->val);
	return(TRUE);
    }
    else
      return(FALSE);
}


/*
 * Return a pointer to an rgb string for the input color. The output is 11
 * characters long and looks like rrr,ggg,bbb.
 *
 * Args    colorName -- The color to convert to ascii rgb.
 *
 * Returns  Pointer to a static buffer containing the rgb string.
 */
char *
color_to_asciirgb(colorName)
    char *colorName;
{
    static char c_to_a_buf[RGBLEN+1];

    struct color_table *ct;

    for(ct = color_tbl; ct && ct->name; ct++)
      if(!strucmp(ct->name, colorName))
	break;
    
    if(ct && ct->name)
      strcpy(c_to_a_buf, ct->rgb);
    else{
	int l;

	/*
	 * If we didn't find the color we're in a bit of trouble. This
	 * most likely means that the user is using the same pinerc on
	 * two terminals, one with more colors than the other. We didn't
	 * find a match because this color isn't present on this terminal.
	 * Since the return value of this function is assumed to be
	 * RGBLEN long, we'd better make it that long.
	 * It still won't work correctly because colors will be screwed up,
	 * but at least the embedded colors in filter.c will get properly
	 * sucked up when they're encountered.
	 */
	strncpy(c_to_a_buf, "xxxxxxxxxxx", RGBLEN);  /* RGBLEN is 11 */
	l = strlen(colorName);
	strncpy(c_to_a_buf, colorName, (l < RGBLEN) ? l : RGBLEN);
	c_to_a_buf[RGBLEN] = '\0';
    }
    
    return(c_to_a_buf);
}

char *
pico_get_last_fg_color()
{
    char *ret = NULL;

    if(_last_fg_color)
      if(ret = (char *)malloc(strlen(_last_fg_color)+1))
	strcpy(ret, _last_fg_color);

    return(ret);
}

char *
pico_get_last_bg_color()
{
    char *ret = NULL;

    if(_last_bg_color)
      if(ret = (char *)malloc(strlen(_last_bg_color)+1))
	strcpy(ret, _last_bg_color);

    return(ret);
}


COLOR_PAIR *
pico_get_cur_color()
{
    return(new_color_pair(_last_fg_color, _last_bg_color));
}


COLOR_PAIR *
new_color_pair(fg, bg)
    char *fg, *bg;
{
    COLOR_PAIR *ret;

    if((ret = (COLOR_PAIR *)malloc(sizeof(*ret))) != NULL){
	memset(ret, 0, sizeof(*ret));
	if(fg){
	    strncpy(ret->fg, fg, MAXCOLORLEN);
	    ret->fg[MAXCOLORLEN] = '\0';
	}

	if(bg){
	    strncpy(ret->bg, bg, MAXCOLORLEN);
	    ret->bg[MAXCOLORLEN] = '\0';
	}
    }

    return(ret);
}


void
free_color_pair(cp)
    COLOR_PAIR **cp;
{
    if(cp && *cp){
	free(*cp);
	*cp = NULL;
    }
}


/*
 * alt_editor - fork off an alternate editor for mail message composition 
 *              if one is configured and passed from pine.  If not, only
 *              ask for the editor if advanced user flag is set, and 
 *              suggest environment's EDITOR value as default.
 */
alt_editor(f, n)
{
    char   eb[NLINE];				/* buf holding edit command */
    char   *fn;					/* tmp holder for file name */
    char   *cp;
    char   *args[MAXARGS];			/* ptrs into edit command */
    char   result[128];				/* result string */
    int	   child, pid, i, done = 0;
    long   l;
    FILE   *p;
    SigType (*ohup) SIG_PROTO((int));
    SigType (*oint) SIG_PROTO((int));
    SigType (*osize) SIG_PROTO((int));
#if	defined(HAVE_WAIT_UNION)
    union  wait stat;
#ifndef	WIFEXITED
#define	WIFEXITED(X)	(!(X).w_termsig)	/* nonzero if child killed */
#endif
#ifndef	WEXITSTATUS
#define	WEXITSTATUS(X)	X.w_retcode		/* child's exit value */
#endif
#else
    int    stat;
#ifndef	WIFEXITED
#define	WIFEXITED(X)	(!((X) & 0xff))		/* low bits, child killed */
#endif
#ifndef	WEXITSTATUS
#define	WEXITSTATUS(X)	((X) >> 8)		/* high bits, exit value */
#endif
#endif

#ifndef	PATH_MAX
#define	PATH_MAX	NLINE
#endif

    if(gmode&MDSCUR){
	emlwrite("Alternate %s not available in restricted mode",
		 f ? "speller" : "editor");
	return(-1);
    }

    strcpy(result, "Alternate %s complete.");

    if(f){
	if(alt_speller)
	  strcpy(eb, alt_speller);
	else
	  return(-1);
    }
    else if(Pmaster == NULL){
	return(-1);
    }
    else{
	eb[0] = '\0';

	if(Pmaster->alt_ed){
	    char **lp, *wsp, *path, fname[PATH_MAX+1];
	    int	   c, rv;

	    for(lp = Pmaster->alt_ed; *lp && **lp; lp++){
		if(wsp = strpbrk(*lp, " \t")){
		    c = *wsp;
		    *wsp = '\0';
		}

		if(strchr(*lp, '/')){
		    rv = fexist(*lp, "x", (off_t *)NULL);
		}
		else{
		    if(!(path = getenv("PATH")))
		      path = ":/bin:/usr/bin";

		    rv = ~FIOSUC;
		    while(rv != FIOSUC && *path && pathcat(fname, &path, *lp))
		      rv = fexist(fname, "x", (off_t *)NULL);
		}

		if(wsp)
		  *wsp = c;

		if(rv == FIOSUC){
		    strcpy(eb, *lp);
		    break;
		}
	    }
	}

	if(!eb[0]){
	    if(!(gmode&MDADVN)){
		emlwrite("\007Unknown Command",NULL);
		return(-1);
	    }

	    if(getenv("EDITOR"))
	      strcpy(eb, (char *)getenv("EDITOR"));
	    else
	      *eb = '\0';

	    while(!done){
		pid = mlreplyd("Which alternate editor ? ", eb,
			       NLINE, QDEFLT, NULL);
		switch(pid){
		  case ABORT:
		    return(-1);
		  case HELPCH:
		    emlwrite("no alternate editor help yet", NULL);

/* 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? */
			mlerase();
			curwp->w_flag |= WFMODE;
			return(-1);
		    }

		    done++;
		    break;
		    default:
		    break;
		}
	    }
	}
    }

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

    strcat(eb, " ");
    strcat(eb, fn);

    cp = eb;
    for(i=0; *cp != '\0';i++){			/* build args array */
	if(i < MAXARGS){
	    args[i] = NULL;			/* in case we break out */
	}
	else{
	    emlwrite("Too many args for command!", NULL);
	    return(-1);
	}

	while(isspace((unsigned char)(*cp)))
	  if(*cp != '\0')
	    cp++;
	  else
	    break;

	args[i] = cp;

	while(!isspace((unsigned char)(*cp)))
	  if(*cp != '\0')
	    cp++;
	  else
	    break;

	if(*cp != '\0')
	  *cp++ = '\0';
    }

    args[i] = NULL;

    for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){
	movecursor(i, 0);
	if(!i){
	    fputs("Invoking alternate ", stdout);
	    fputs(f ? "speller..." : "editor...", stdout);
	}

	peeol();
    }

    (*term.t_flush)();
    if(Pmaster)
      (*Pmaster->tty_fix)(0);

#ifdef	SIGCHLD
    if(Pmaster){
	/*
	 * The idea here is to keep any mail connections our caller
	 * may have open while our child's out running around...
	 */
	pico_child_done = pico_child_jmp_ok = 0;
	(void) signal(SIGCHLD,  child_handler);
    }
#endif

    if((child = fork()) > 0){		/* wait for the child to finish */
	ohup = signal(SIGHUP, SIG_IGN);	/* ignore signals for now */
	oint = signal(SIGINT, SIG_IGN);
#ifdef	RESIZING
        osize = signal(SIGWINCH, SIG_IGN);
#endif

#ifdef	SIGCHLD
	if(Pmaster){
	    while(!pico_child_done){
		(*Pmaster->newmail)(0, 0);
		if(!pico_child_done){
		    if(setjmp(pico_child_state) == 0){
			pico_child_jmp_ok = 1;
			sleep(600);
		    }
		    else
		      our_sigunblock(SIGCHLD);
		}

		pico_child_jmp_ok = 0;
	    }
	}
#endif

	while((pid = (int) wait(&stat)) != child)
	  ;

	signal(SIGHUP, ohup);	/* restore signals */
	signal(SIGINT, oint);
#ifdef	RESIZING
        signal(SIGWINCH, osize);
#endif

	/*
	 * Report child's unnatural or unhappy exit...
	 */
	if(WIFEXITED(stat) && WEXITSTATUS(stat) == 0)
	  strcpy(result, "Alternate %s done");
	else
	  sprintf(result, "Alternate %%s abnormally terminated (%d)",
		  WIFEXITED(stat) ? WEXITSTATUS(stat) : -1);
    }
    else if(child == 0){		/* spawn editor */
	signal(SIGHUP, SIG_DFL);	/* let editor handle signals */
	signal(SIGINT, SIG_DFL);
#ifdef	RESIZING
        signal(SIGWINCH, SIG_DFL);
#endif
#ifdef	SIGCHLD
	(void) signal(SIGCHLD,  SIG_DFL);
#endif
	if(execvp(args[0], args) < 0)
	  exit(-1);
    }
    else				/* error! */
      sprintf(result, "\007Can't fork %%s: %s", errstr(errno));

#ifdef	SIGCHLD
    (void) signal(SIGCHLD,  SIG_DFL);
#endif

    if(Pmaster)
      (*Pmaster->tty_fix)(1);

    /*
     * replace edited text with new text 
     */
    curbp->b_flag &= ~BFCHG;		/* make sure old text gets blasted */
    readin(fn, 0);			/* read new text overwriting old */
    unlink(fn);				/* blast temp file */
    curbp->b_flag |= BFCHG;		/* mark dirty for packbuf() */
    ttopen();				/* reset the signals */
    refresh(0, 1);			/* redraw */
    update();
    emlwrite(result, f ? "speller" : "editor");
    return(0);
}



int
pathcat(buf, path, file)
    char *buf, **path, *file;
{
    register int n = 0;

    while(**path && **path != ':'){
	if(n++ > PATH_MAX)
	  return(FALSE);

	*buf++ = *(*path)++;
    }

    if(n){
	if(n++ > PATH_MAX)
	  return(FALSE);

	*buf++ = '/';
    }

    while(*buf = *file++){
	if(n++ > PATH_MAX)
	  return(FALSE);

	buf++;
    }

    if(**path == ':')
      (*path)++;

    return(TRUE);
}
    



/*
 *  bktoshell - suspend and wait to be woken up
 */
int
bktoshell()		/* suspend MicroEMACS and wait to wake up */
{
#ifdef	SIGTSTP
    int pid;

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

    if(Pmaster){
	if(!Pmaster->suspend){
	    emlwrite("\007Unknown command: ^Z", NULL);
	    return(0);
	}

	if((*Pmaster->suspend)() == NO_OP_COMMAND){
	    int rv;
	
	    if(km_popped){
		term.t_mrow = 2;
		curwp->w_ntrows -= 2;
	    }

	    clearcursor();
	    mlerase();
	    rv = (*Pmaster->showmsg)('x');
	    ttresize();
	    picosigs();
	    if(rv)		/* Did showmsg corrupt the display? */
	      refresh(0, 1);	/* Yes, repaint */

	    mpresf = 1;
	    if(km_popped){
		term.t_mrow = 0;
		curwp->w_ntrows += 2;
	    }
	}
	else{
	    ttresize();
	    pclear(0, term.t_nrow);
	    refresh(0, 1);
	}

	return(1);
    }

    if(gmode&MDSPWN){
	char *shell;

	vttidy();
	movecursor(0, 0);
	(*term.t_eeop)();
	printf("\n\n\nUse \"exit\" to return to Pi%s\n",
	       (gmode & MDBRONLY) ? "lot" : "co");
	system((shell = (char *)getenv("SHELL")) ? shell : "/bin/csh");
	rtfrmshell SIG_PROTO((dummy));	/* fixup tty */
    }
    else {
	movecursor(term.t_nrow-1, 0);
	peeol();
	movecursor(term.t_nrow, 0);
	peeol();
	movecursor(term.t_nrow, 0);
	printf("\n\n\nUse \"fg\" to return to Pi%s\n",
	       (gmode & MDBRONLY) ? "lot" : "co");
	ttclose();
	movecursor(term.t_nrow, 0);
	peeol();
	(*term.t_flush)();

	signal(SIGCONT, rtfrmshell);	/* prepare to restart */
	signal(SIGTSTP, SIG_DFL);			/* prepare to stop */
	kill(0, SIGTSTP);
    }

    return(1);
#endif
}


/* 
 * rtfrmshell - back from shell, fix modes and return
 */
SigType
rtfrmshell SIG_PROTO((int sig))
{
#ifdef	SIGCONT
    signal(SIGCONT, SIG_DFL);
    ttopen();
    ttresize();
    pclear(0, term.t_nrow);
    refresh(0, 1);
#endif
}



/*
 * do_hup_signal - jump back in the stack to where we can handle this
 */
SigType
do_hup_signal SIG_PROTO((int sig))
{
    signal(SIGHUP,  SIG_IGN);			/* ignore further SIGHUP's */
    signal(SIGTERM, SIG_IGN);			/* ignore further SIGTERM's */
    if(Pmaster){
	extern jmp_buf finstate;

	longjmp(finstate, COMP_GOTHUP);
    }
    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, TRUE);
	}
	vttidy();
	exit(1);
    }
}


/*
 * big bitmap of ASCII characters allowed in a file name
 * (needs reworking for other char sets)
 */
unsigned char okinfname[32] = {
      0,    0, 			/* ^@ - ^G, ^H - ^O  */
      0,    0,			/* ^P - ^W, ^X - ^_  */
      0x80, 0x17,		/* SP - ' ,  ( - /   */
      0xff, 0xc4,		/*  0 - 7 ,  8 - ?   */
      0x7f, 0xff,		/*  @ - G ,  H - O   */
      0xff, 0xe1,		/*  P - W ,  X - _   */
      0x7f, 0xff,		/*  ` - g ,  h - o   */
      0xff, 0xf6,		/*  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 with mode passed in m, 
 *          FALSE otherwise.  By side effect returns length of file in l
 */
fexist(file, m, l)
char  *file;
char  *m;			/* files mode: r,w,rw,t or x */
off_t *l;			/* t means use lstat         */
{
    struct stat	sbuf;
    extern int lstat();
    int		(*stat_f)() = (m && *m == 't') ? lstat : stat;
    int accessible;
/* define them here so we don't have to worry about what to include */
#define EXECUTE_ACCESS  01
#define WRITE_ACCESS    02
#define READ_ACCESS     04

    if(l)
      *l = (off_t)0;
    
    if((*stat_f)(file, &sbuf) < 0){
	switch(errno){
	  case ENOENT :				/* File not found */
	    return(FIOFNF);
#ifdef	ENAMETOOLONG
	  case ENAMETOOLONG :			/* Name is too long */
	    return(FIOLNG);
#endif
	  case EACCES :				/* File not found */
	    return(FIOPER);
	  default:				/* Some other error */
	    return(FIOERR);
	}
    }

    if(l)
      *l = (off_t)sbuf.st_size;

    if((sbuf.st_mode&S_IFMT) == S_IFDIR)
      return(FIODIR);
    else if(*m == 't'){
	struct stat	sbuf2;

	/*
	 * If it is a symbolic link pointing to a directory, treat
	 * it like it is a directory, not a link.
	 */
	if((sbuf.st_mode&S_IFMT) == S_IFLNK){
	    if(stat(file, &sbuf2) < 0){
		switch(errno){
		  case ENOENT :				/* File not found */
		    return(FIOSYM);			/* call it a link */
#ifdef	ENAMETOOLONG
		  case ENAMETOOLONG :			/* Name is too long */
		    return(FIOLNG);
#endif
		  case EACCES :				/* File not found */
		    return(FIOPER);
		  default:				/* Some other error */
		    return(FIOERR);
		}
	    }

	    if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
	      return(FIODIR);
	}

	return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
    }

    if(*m == 'r'){				/* read access? */
	if(*(m+1) == 'w')			/* and write access? */
	  return((access(file,READ_ACCESS)==0)
		 ? (access(file,WRITE_ACCESS)==0)
		    ? FIOSUC
		    : FIONWT
		 : FIONRD);
	else if(!*(m+1))			/* just read access? */
	  return((access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD);
    }
    else if(*m == 'w' && !*(m+1))		/* write access? */
      return((access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT);
    else if(*m == 'x' && !*(m+1))		/* execute access? */
      return((access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX);
    return(FIOERR);				/* bad m arg */
}


/*
 * 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, d)
char *fn;
long *l;
time_t *d;
{
    struct stat sbuf;

    if(l)
      *l = 0;

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

    if(l)
      *l = sbuf.st_size;

    if(d)
      *d = sbuf.st_mtime;

    return((sbuf.st_mode&S_IFMT) == S_IFDIR);
}


/*
 * gethomedir - returns the users home directory
 *              Note: home is malloc'd for life of pico
 */
char *
gethomedir(l)
int *l;
{
    static char *home = NULL;
    static short hlen = 0;

    if(home == NULL){
	char buf[NLINE];
	strcpy(buf, "~");
	fixpath(buf, NLINE);		/* let fixpath do the work! */
	hlen = strlen(buf);
	if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
	    emlwrite("Problem allocating space for home dir", NULL);
	    return(0);
	}

	strcpy(home, buf);
    }

    if(l)
      *l = hlen;

    return(home);
}


/*
 * homeless - returns true if given file does not reside in the current
 *            user's home directory tree. 
 */
homeless(f)
char *f;
{
    char *home;
    int   len;

    home = gethomedir(&len);
    return(strncmp(home, f, len));
}



/*
 * 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;
{
    return((err >= 0 && err < sys_nerr) ? (char *)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, pat, n, e)
char *dn, *pat, *e;
int  *n;
{
    long           l;
    char          *names, *np, *p;
    struct stat    sbuf;
#if	defined(ct)
    FILE          *dirp;
    char           fn[DIRSIZ+1];
#else
    DIR           *dirp;			/* opened directory */
#endif
#ifdef	USE_DIRENT
    struct dirent *dp;
#else
    struct direct *dp;
#endif

    *n = 0;

    if(stat(dn, &sbuf) < 0){
	switch(errno){
	  case ENOENT :				/* File not found */
	    if(e)
	      sprintf(e, "\007File not found: \"%s\"", dn);

	    break;
#ifdef	ENAMETOOLONG
	  case ENAMETOOLONG :			/* Name is too long */
	    if(e)
	      sprintf(e, "\007File name too long: \"%s\"", dn);

	    break;
#endif
	  default:				/* Some other error */
	    if(e)
	      sprintf(e, "\007Error getting file info: \"%s\"", dn);

	    break;
	}
	return(NULL);
    } 
    else{
	l = sbuf.st_size;
	if((sbuf.st_mode&S_IFMT) != S_IFDIR){
	    if(e)
	      sprintf(e, "\007Not a directory: \"%s\"", dn);

	    return(NULL);
	}
    }

    if((names=(char *)malloc(sizeof(char)*l)) == NULL){
	if(e)
	  sprintf(e, "\007Can't malloc space for file names");

	return(NULL);
    }

    errno = 0;
    if((dirp=opendir(dn)) == NULL){
	if(e)
	  sprintf(e, "\007Can't open \"%s\": %s", dn, errstr(errno));

	free((char *)names);
	return(NULL);
    }

    np = names;

#if	defined(ct)
    while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
    /* skip empty slots with inode of 0 */
	if(dp.d_ino == 0)
	     continue;
	(*n)++;                     /* count the number of active slots */
	(void)strncpy(fn, dp.d_name, DIRSIZ);
	fn[14] = '\0';
	p = fn;
	while((*np++ = *p++) != '\0')
	  ;
    }
#else
    while((dp = readdir(dirp)) != NULL)
      if(!pat || !*pat || !strncmp(dp->d_name, pat, strlen(pat))){
	  (*n)++;
	  p = dp->d_name;
	  while(*np++ = *p++)
	    ;
      }
#endif

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


/*
 * fioperr - given the error number and file name, display error
 */
void
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 FIOPER:
	emlwrite("\007Permission 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;
    register char *x, *y, *z;
    char *home = NULL;
    char name[20];
    
    if(*fn == '~') {
        for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++)
	  ;

        *y = '\0';
        if(x == fn + 1){			/* ~/ */
	    if (!(home = (char *) getenv("HOME")))
	      if (pw = getpwuid(geteuid()))
		home = pw->pw_dir;
	}
	else if(*name){				/* ~username/ */
	    if(pw = getpwnam(name))
	      home = pw->pw_dir;
	}

        if(!home || (strlen(home) + strlen(fn) > len))
	  return(NULL);

	/* make room for expanded path */
	for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
	    z >= x;
	    *y-- = *z--)
	  ;

	/* and insert the expanded address */
	for(x = fn, y = home; *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;

    /* filenames relative to ~ */
    if(!((name[0] == '/')
          || (name[0] == '.'
              && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
	if(Pmaster && !(gmode&MDCURDIR)
                   && (*name != '~' && strlen(name)+2 <= len)){

	    if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 <= len){
		int i, off = strlen(opertree);

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

		strncpy(name, opertree, off);
		name[off] = '/';
	    }
	    else{
		for(shft = strchr(name, '\0'); shft >= name; shft--)
		  shft[2] = *shft;

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

	pfnexpand(name, len);
    }
}


/*
 * compresspath - given a base path and an additional directory, collapse
 *                ".." and "." elements and return absolute path (appending
 *                base if necessary).  
 *
 *                returns  1 if OK, 
 *                         0 if there's a problem
 *                         new path, by side effect, if things went OK
 */
compresspath(base, path, len)
char *base, *path;
int  len;
{
    register int i;
    int  depth = 0;
    char *p;
    char *stack[32];
    char  pathbuf[NLINE];

#define PUSHD(X)  (stack[depth++] = X)
#define POPD()    ((depth > 0) ? stack[--depth] : "")

    if(*path == '~'){
	fixpath(path, len);
	strcpy(pathbuf, path);
    }
    else if(*path != C_FILESEP)
      sprintf(pathbuf, "%s%c%s", base, C_FILESEP, path);
    else
      strcpy(pathbuf, path);

    p = &pathbuf[0];
    for(i=0; pathbuf[i] != '\0'; i++){		/* pass thru path name */
	if(pathbuf[i] == '/'){
	    if(p != pathbuf)
	      PUSHD(p);				/* push dir entry */

	    p = &pathbuf[i+1];			/* advance p */
	    pathbuf[i] = '\0';			/* cap old p off */
	    continue;
	}

	if(pathbuf[i] == '.'){			/* special cases! */
	    if(pathbuf[i+1] == '.' 		/* parent */
	       && (pathbuf[i+2] == '/' || pathbuf[i+2] == '\0')){
		if(!strcmp(POPD(), ""))		/* bad news! */
		  return(0);

		i += 2;
		p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
	    }
	    else if(pathbuf[i+1] == '/' || pathbuf[i+1] == '\0'){
		i++;
		p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
	    }
	}
    }

    if(*p != '\0')
      PUSHD(p);					/* get last element */

    path[0] = '\0';
    for(i = 0; i < depth; i++){
	strcat(path, S_FILESEP);
	strcat(path, stack[i]);
    }

    return(1);					/* everything's ok */
}


/*
 * This routine is derived from BSD4.3 code,
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 */
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)tmpnam.c	4.5 (Berkeley) 6/27/88";
#endif /* LIBC_SCCS and not lint */
/*----------------------------------------------------------------------
      Return a unique file name in a given directory.  This is not quite
      the same as the usual tempnam() function, though it is very similar.
      We want it to use the TMP environment variable only if dir is NULL,
      instead of using TMP regardless if it is set.

  Args: dir      -- The directory to create the name in
        prefix   -- Prefix of the name
 
 Result: Malloc'd string equal to new name is returned.  It must be free'd
	 by the caller.  Returns the string on success and NULL on failure.
  ----*/
char *
temp_nam(dir, prefix)
    char *dir, *prefix;
{
    struct stat buf;
    int  l;
    char *f, *name;
    char *our_mktemp();

    if (!(name = (char *)malloc((unsigned int)NFILEN)))
        return((char *)NULL);

    if (!dir && (f = getenv("TMPDIR")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !access(f, WRITE_ACCESS|EXECUTE_ACCESS)) {
        (void)strcpy(name, f);
        goto done;
    }

    if (dir && !stat(dir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
	                 !access(dir, WRITE_ACCESS|EXECUTE_ACCESS)) {
        (void)strcpy(name, dir);
        goto done;
    }

#ifndef P_tmpdir
#define	P_tmpdir	"/usr/tmp"
#endif
    if (!stat(P_tmpdir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !access(P_tmpdir, WRITE_ACCESS|EXECUTE_ACCESS)) {
        (void)strcpy(name, P_tmpdir);
        goto done;
    }

    if (!stat("/tmp", &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !access("/tmp", WRITE_ACCESS|EXECUTE_ACCESS)) {
        (void)strcpy(name, "/tmp");
        goto done;
    }
    free((void *)name);
    return((char *)NULL);

done:
    if(*((f = &name[strlen(name)]) - 1) != '/')
      *f++ = '/';

    if(prefix && (l = strlen(prefix))){
	strcpy(f, prefix);
	f += l;
    }

    strcpy(f, "XXXXXX");
    return(our_mktemp(name));
}


/*
 * This routine is derived from BSD4.3 code,
 * Copyright (c) 1987 Regents of the University of California.
 * All rights reserved.
 *
 * We use this instead of mktemp() since we know of at least one stupid
 * mktemp() (AIX3.2) which breaks things.
 */
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)mktemp.c	5.7 (Berkeley) 6/27/88";
#endif /* LIBC_SCCS and not lint */

static
_gettemp(as)
	char	*as;
{
	extern int	errno;
	register char	*start, *trv;
	struct stat	sbuf;
	unsigned	pid;

	pid = (unsigned)getpid();

	/* extra X's get set to 0's */
	for (trv = as; *trv; ++trv);
	while (*--trv == 'X') {
		*trv = (pid % 10) + '0';
		pid /= 10;
	}

	/*
	 * check for write permission on target directory; if you have
	 * six X's and you can't write the directory, this will run for
	 * a *very* long time.
	 */
	for (start = ++trv; trv > as && *trv != '/'; --trv);
	if (*trv == '/') {
		*trv = '\0';
		if (stat(as==trv ? "/" : as, &sbuf)
		    || !(sbuf.st_mode & S_IFDIR))
			return(0);
		*trv = '/';
	}
	else if (stat(".", &sbuf) == -1)
		return(0);

	for (;;) {
		if (stat(as, &sbuf))
			return(errno == ENOENT ? 1 : 0);

		/* tricky little algorithm for backward compatibility */
		for (trv = start;;) {
			if (!*trv)
				return(0);
			if (*trv == 'z')
				*trv++ = 'a';
			else {
				if (isdigit((unsigned char)*trv))
					*trv = 'a';
				else
					++*trv;
				break;
			}
		}
	}
	/*NOTREACHED*/
}

char *
our_mktemp(as)
	char	*as;
{
	return(_gettemp(as) ? as : (char *)NULL);
}

/*
 * tmpname - return a temporary file name in the given buffer, the filename
 * is in the directory dir unless dir is NULL
 */
void
tmpname(dir, name)
char *dir;
char *name;
{
    char *t;

    if(t = temp_nam((dir && *dir) ? dir : NULL, "pico.")){
	strcpy(name, t);
	free((void *)t);
    } 
    else {
	emlwrite("Unable to construct temp file name", NULL);
	name[0] = '\0';
    }
}


/*
 * 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.
 */
void
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;
}


/*
 * copy - copy contents of file 'a' into a file named 'b'.  Return error
 *        if either isn't accessible or is a directory
 */
copy(a, b)
char *a, *b;
{
    int    in, out, n, rv = 0;
    char   *cb;
    struct stat tsb, fsb;
    extern int  errno;

    if(stat(a, &fsb) < 0){		/* get source file info */
	emlwrite("Can't Copy: %s", errstr(errno));
	return(-1);
    }

    if(!(fsb.st_mode&S_IREAD)){		/* can we read it? */
	emlwrite("\007Read permission denied: %s", a);
	return(-1);
    }

    if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
	emlwrite("\007Can't copy: %s is a directory", a);
	return(-1);
    }

    if(stat(b, &tsb) < 0){		/* get dest file's mode */
	switch(errno){
	  case ENOENT:
	    break;			/* these are OK */
	  default:
	    emlwrite("\007Can't Copy: %s", errstr(errno));
	    return(-1);
	}
    }
    else{
	if(!(tsb.st_mode&S_IWRITE)){	/* can we write it? */
	    emlwrite("\007Write permission denied: %s", b);
	    return(-1);
	}

	if((tsb.st_mode&S_IFMT) == S_IFDIR){	/* is it directory? */
	    emlwrite("\007Can't copy: %s is a directory", b);
	    return(-1);
	}

	if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
	    emlwrite("\007Identical files.  File not copied", NULL);
	    return(-1);
	}
    }

    if((in = open(a, O_RDONLY)) < 0){
	emlwrite("Copy Failed: %s", errstr(errno));
	return(-1);
    }

    if((out=creat(b, fsb.st_mode&0xfff)) < 0){
	emlwrite("Can't Copy: %s", errstr(errno));
	close(in);
	return(-1);
    }

    if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
	emlwrite("Can't allocate space for copy buffer!", NULL);
	close(in);
	close(out);
	return(-1);
    }

    while(1){				/* do the copy */
	if((n = read(in, cb, NLINE)) < 0){
	    emlwrite("Can't Read Copy: %s", errstr(errno));
	    rv = -1;
	    break;			/* get out now */
	}

	if(n == 0)			/* done! */
	  break;

	if(write(out, cb, n) != n){
	    emlwrite("Can't Write Copy: %s", errstr(errno));
	    rv = -1;
	    break;
	}
    }

    free(cb);
    close(in);
    close(out);
    return(rv);
}


/*
 * Open a file for writing. Return TRUE if all is well, and FALSE on error
 * (cannot create).
 */
ffwopen(fn, readonly)
char    *fn;
int	 readonly;
{
    int		 fd;
    extern FIOINFO g_pico_fio;
#ifndef	MODE_READONLY
#define	MODE_READONLY	(0600)
#endif

    /*
     * Call open() by hand since we don't want O_TRUNC -- it'll
     * screw over-quota users.  The strategy is to overwrite the
     * existing file's data and call ftruncate before close to lop
     * off bytes 
     */

    g_pico_fio.flags = FIOINFO_WRITE;
    g_pico_fio.name  = fn;
    if((fd = open(fn, O_CREAT|O_WRONLY, readonly ? MODE_READONLY : 0666)) >= 0
       && (g_pico_fio.fp = fdopen(fd, "w")) != NULL
       && fseek(g_pico_fio.fp, 0L, 0) == 0)
      return (FIOSUC);

    emlwrite("Cannot open file for writing: %s", errstr(errno));
    return (FIOERR);
}


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

    errno = 0;
    if((g_pico_fio.flags & FIOINFO_WRITE)
       && (fflush(g_pico_fio.fp) == EOF
	   || ftruncate(fileno(g_pico_fio.fp),
			(off_t) ftell(g_pico_fio.fp)) < 0)){
	emlwrite("\007Error preparing to close file: %s", errstr(errno));
	sleep(5);
    }

    if (fclose(g_pico_fio.fp) == EOF) {
        emlwrite("\007Error closing file: %s", errstr(errno));
        return(FIOERR);
    }

    return(FIOSUC);
}



#define	EXTEND_BLOCK	1024


/*
 * ffelbowroom - make sure the destination's got enough room to receive
 *		 what we're about to write...
 */
ffelbowroom()
{
    register LINE *lp;
    register long  n;
    int		   x;
    char	   s[EXTEND_BLOCK], *errstring = NULL;
    struct stat	   fsbuf;
    extern FIOINFO g_pico_fio;

    /* Figure out how much room do we need */
    /* first, what's total */
    for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
      n += (llength(lp) + 1);

    errno = 0;			/* make sure previous error are cleared */
    
    if(fstat(fileno(g_pico_fio.fp), &fsbuf) == 0){
	n -= fsbuf.st_size;

	if(n > 0L){			/* must be growing file, extend it */
	    memset(s, 'U', EXTEND_BLOCK);
	    if(fseek(g_pico_fio.fp, fsbuf.st_size, 0) == 0){
		for( ; n > 0L; n -= EXTEND_BLOCK){
		    x = (n < EXTEND_BLOCK) ? (int) n : EXTEND_BLOCK;
		    if(fwrite(s, x * sizeof(char), 1, g_pico_fio.fp) != 1){
			errstring = errstr(errno);
			break;
		    }
		}

		if(!errstring
		   && (fflush(g_pico_fio.fp) == EOF
		       || fsync(fileno(g_pico_fio.fp)) < 0))
		  errstring = errstr(errno);

		if(errstring)			/* clean up */
		  (void) ftruncate(fileno(g_pico_fio.fp), fsbuf.st_size);
		else if(fseek(g_pico_fio.fp, 0L, 0) != 0)
		  errstring = errstr(errno);
	    }
	    else
	      errstring = errstr(errno);
	}
    }
    else
      errstring = errstr(errno);

    if(errstring){
	sprintf(s, "Error writing to %s: %s", g_pico_fio.name, errstring);
	emlwrite(s, NULL);
	(void) fclose(g_pico_fio.fp);
	return(FALSE);
    }

    return(TRUE);
}


/*
 * 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], *p;

    if(p = (char *)getenv("MAIL"))
      sprintf(inbox, p);
    else
      sprintf(inbox,"%s/%s", MAILDIR, 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 > timeo
 */
time_to_check()
{
    static time_t lasttime = 0L;

    if(!timeo)
      return(FALSE);

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


/*
 * sstrcasecmp - compare two pointers to strings case independently
 */
int
sstrcasecmp(s1, s2)
    const QSType *s1, *s2;
{
    return((*pcollator)(*(char **)s1, *(char **)s2));
}


/*--------------------------------------------------
     A case insensitive strcmp()     
  
   Args: o, r -- The two strings to compare

 Result: integer indicating which is greater
  ---*/
int
strucmp(o, r)
    register char *o, *r;
{
    if(o == NULL){
	if(r == NULL)
	  return 0;
	else
	  return -1;
    }
    else if(r == NULL)
      return 1;

    while(*o && *r
	  && ((isupper((unsigned char)(*o))
				  ? (unsigned char)tolower((unsigned char)(*o))
				  : (unsigned char)(*o))
	     == (isupper((unsigned char)(*r))
				  ? (unsigned char)tolower((unsigned char)(*r))
				  : (unsigned char)(*r)))){
	o++;
	r++;
    }

    return((isupper((unsigned char)(*o))
				? tolower((unsigned char)(*o))
				: (int)(unsigned char)(*o))
	   - (isupper((unsigned char)(*r))
			        ? tolower((unsigned char)(*r))
				: (int)(unsigned char)(*r)));
}


/*----------------------------------------------------------------------
     A case insensitive strncmp()     
  
   Args: o, r -- The two strings to compare
         n    -- length to stop comparing strings at

 Result: integer indicating which is greater
   
  ----*/
int
struncmp(o, r, n)
    register char *o,
		  *r;
    register int   n;
{
    if(n < 1)
      return 0;

    if(o == NULL){
	if(r == NULL)
	  return 0;
	else
	  return -1;
    }
    else if(r == NULL)
      return 1;

    n--;
    while(n && *o && *r
	  && ((isupper((unsigned char)(*o))
				  ? (unsigned char)tolower((unsigned char)(*o))
				  : (unsigned char)(*o))
	     == (isupper((unsigned char)(*r))
				  ? (unsigned char)tolower((unsigned char)(*r))
				  : (unsigned char)(*r)))){
	o++;
	r++;
	n--;
    }

    return((isupper((unsigned char)(*o))
				? tolower((unsigned char)(*o))
				: (int)(unsigned char)(*o))
	   - (isupper((unsigned char)(*r))
			        ? tolower((unsigned char)(*r))
				: (int)(unsigned char)(*r)));
}


/*
 * chkptinit -- initialize anything we need to support composer
 *		checkpointing
 */
chkptinit(file, n)
    char *file;
    int   n;
{
    unsigned pid;
    char    *chp;

    if(!file[0]){
	long gmode_save = gmode;

	if(gmode&MDCURDIR)
	  gmode &= ~MDCURDIR;  /* so fixpath will use home dir */

	strcpy(file, "#picoXXXXX#");
	fixpath(file, NLINE);
	gmode = gmode_save;
    }
    else{
	int l = strlen(file);

	if(file[l-1] != '/'){
	    file[l++] = '/';
	    file[l]   = '\0';
	}

	strcpy(file + l, "#picoXXXXX#");
    }

    pid = (unsigned)getpid();
    for(chp = file+strlen(file) - 2; *chp == 'X'; chp--){
	*chp = (pid % 10) + '0';
	pid /= 10;
    }

    unlink(file);
}

void
set_collation()
{
    char *status = NULL;
    extern int collator();  /* strcoll isn't declared on all systems */

    pcollator = strucmp;

#ifdef LC_COLLATE
    /*
     * This may not have the desired effect, if collator is not
     * defined to be strcoll in os.h and strcmp and friends
     * don't know about locales. If your system does have strcoll
     * but we haven't defined collator to be strcoll in os.h, let us know.
     */
    status = setlocale(LC_COLLATE, "");

    /*
     * If there is an error or if the locale is the "C" locale, then we
     * don't want to use strcoll because in the default "C" locale strcoll
     * uses strcmp ordering and we want strucmp ordering.
     *
     * The problem with this is that setlocale returns a string which is
     * not equal to "C" on some systems even when the locale is "C", so we
     * can't really tell on those systems. On some systems like that, we
     * may end up with a strcmp-style collation instead of a strucmp-style.
     * We recommend that the users of those systems explicitly set
     * LC_COLLATE in their environment.
     */
    if(status && !(status[0] == 'C' && status[1] == '\0'))
      pcollator = collator;
#endif
#ifdef LC_CTYPE
    (void)setlocale(LC_CTYPE, "");
#endif
}


#ifdef	RESIZING
/*
 * winch_handler - handle window change signal
 */
SigType winch_handler SIG_PROTO((int sig))
{
    signal(SIGWINCH, winch_handler);
    ttresize();
    if(Pmaster && Pmaster->winch_cleanup && Pmaster->arm_winch_cleanup)
      (*Pmaster->winch_cleanup)();
}
#endif	/* RESIZING */


#ifdef	SIGCHLD
/*
 * child_handler - handle child status change signal
 */
SigType child_handler SIG_PROTO ((int sig))
{
    pico_child_done = 1;
    if(pico_child_jmp_ok){
#ifdef	sco
	/*
	 * Villy Kruse <vek@twincom.nl> says:
	 * This probably only affects older unix systems with a "broken" sleep             
	 * function such as AT&T System V rel 3.2 and systems derived from
	 * that version. The problem is that the sleep function on these
	 * versions of unix will set up a signal handler for SIGALRM which
	 * will longjmp into the sleep function when the alarm goes off.
	 * This gives problems if another signal handler longjmps out of the
	 * sleep function, thus making the stack frame for the signal function
	 * invalid, and when the ALARM handler later longjmps back into the
	 * sleep function it does no longer have a valid stack frame.
	 * My sugested fix is to cancel the pending alarm in the SIGCHLD
	 * handler before longjmp'ing. This shouldn't hurt as there
	 * shouldn't be any ALARM pending at this point except possibly from
	 * the sleep call.
	 *
	 *
	 * [Even though it shouldn't hurt, we'll make it sco-specific for now.]
	 *
	 *
	 * The sleep call might have set up a signal handler which would
	 * longjmp back into the sleep code, and that would cause a crash.
	 */
	signal(SIGALRM, SIG_IGN);	/* Cancel signal handeler */
	alarm(0);			/* might longjmp back into sleep */ 
#endif
	longjmp(pico_child_state, 1);
    }
}
#endif	/* SIGCHLD */


#ifdef	POSIX_SIGNALS
/*----------------------------------------------------------------------
   Reset signals after critical imap code
 ----*/
SigType
(*posix_signal(sig_num, action))()
    int	    sig_num;
    SigType (*action)();
{
    struct sigaction new_action, old_action;

    memset((void *)&new_action, 0, sizeof(struct sigaction));
    sigemptyset (&new_action.sa_mask);
    new_action.sa_handler = action;
#ifdef	SA_RESTART
    new_action.sa_flags = SA_RESTART;
#else
    new_action.sa_flags = 0;
#endif
    sigaction(sig_num, &new_action, &old_action);
    return(old_action.sa_handler);
}

int
posix_sigunblock(mask)
    int mask;
{
    sigset_t sig_mask;

    sigemptyset(&sig_mask);
    sigaddset(&sig_mask, mask);
    return(sigprocmask(SIG_UNBLOCK, &sig_mask, NULL));
}
#endif /* POSIX_SIGNALS */


#if	defined(sv3) || defined(ct)
/* Placed by rll to handle the rename function not found in AT&T */
rename(oldname, newname)
    char *oldname;
    char *newname;
{
    int rtn;

    if ((rtn = link(oldname, newname)) != 0) {
	perror("Was not able to rename file.");
	return(rtn);
    }

    if ((rtn = unlink(oldname)) != 0)
      perror("Was not able to unlink file.");

    return(rtn);
}
#endif


#ifdef	MOUSE

/* 
 * init_mouse - check for xterm and initialize mouse tracking if present...
 */
init_mouse()
{
    if(mexist)
      return(TRUE);

    if(getenv("DISPLAY")){
	mouseon();
        kpinsert("\033[M", KEY_XTERM_MOUSE, 1);
	return(mexist = TRUE);
    }
    else
      return(FALSE);
}


/* 
 * end_mouse - clear xterm mouse tracking if present...
 */
void
end_mouse()
{
    if(mexist){
	mexist = 0;			/* just see if it exists here. */
	mouseoff();
    }
}


/*
 * mouseexist - function to let outsiders know if mouse is turned on
 *              or not.
 */
mouseexist()
{
    return(mexist);
}


/*
 * mouseon - call made available for programs calling pico to turn ON the
 *           mouse cursor.
 */
void
mouseon()
{
    fputs(XTERM_MOUSE_ON, stdout);
}


/*
 * mouseon - call made available for programs calling pico to turn OFF the
 *           mouse cursor.
 */
void
mouseoff()
{
    fputs(XTERM_MOUSE_OFF, stdout);
}


/* 
 * checkmouse - look for mouse events in key menu and return 
 *              appropriate value.
 */
int
checkmouse(ch, down, mcol, mrow)
unsigned *ch;
int	  down, mcol, mrow;
{
    static int oindex;
    register int k;			/* current bit/button of mouse */
    int i = 0, rv = 0;
    MENUITEM *mp;

    if(!mexist || mcol < 0 || mrow < 0)
      return(FALSE);

    if(down)			/* button down */
      oindex = -1;

    for(mp = mfunc; mp; mp = mp->next)
      if(mp->action && M_ACTIVE(mrow, mcol, mp))
	break;

    if(mp){
	unsigned long r;

	r = (*mp->action)(down ? M_EVENT_DOWN : M_EVENT_UP,
			  mrow, mcol, M_BUTTON_LEFT, 0);
	if(r & 0xffff){
	    *ch = (unsigned)((r>>16)&0xffff);
	    rv  = TRUE;
	}
    }
    else{
	while(1){			/* see if we understand event */
	    if(i >= 12){
		i = -1;
		break;
	    }

	    if(M_ACTIVE(mrow, mcol, &menuitems[i]))
	      break;

	    i++;
	}

	if(down){			/* button down */
	    oindex = i;			/* remember where */
	    if(i != -1
	       && menuitems[i].label_hiliter != NULL
	       && menuitems[i].val != mnoop)  /* invert label */
	      (*menuitems[i].label_hiliter)(1, &menuitems[i]);
	}
	else{				/* button up */
	    if(oindex != -1){
		if(i == oindex){
		    *ch = menuitems[i].val;
		    rv = TRUE;
		}
	    }
	}
    }

    /* restore label */
    if(!down
       && oindex != -1
       && menuitems[oindex].label_hiliter != NULL
       && menuitems[oindex].val != mnoop)
      (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);

    return(rv);
}


/*
 * invert_label - highlight the label of the given menu item.
 */
void
invert_label(state, m)
int state;
MENUITEM *m;
{
    unsigned i, j;
    int   r, c, p, oldr, oldc, col_offset;
    int   savettrow, savettcol;
    char *lp;

    get_cursor(&savettrow, &savettcol);

    /*
     * Leave the command name bold
     */
    col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
    movecursor((int)m->tl.r, (int)m->tl.c + col_offset);
    flip_inv(state);

    for(i = m->tl.r; i <= m->br.r; i++)
      for(j = m->tl.c + col_offset; j <= m->br.c; j++)
	if(i == m->lbl.r && j == m->lbl.c + col_offset && m->label){
	    lp = m->label + col_offset;		/* show label?? */
	    while(*lp && j++ < m->br.c)
	      putc(*lp++, stdout);

	    continue;
	}
	else
	  putc(' ', stdout);

    if(state)
      flip_inv(FALSE);

    movecursor(savettrow, savettcol);
}
#endif	/* MOUSE */


