int timeo = 0;
time_t time_of_last_input;
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;
    char *canonical_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, *the_normal_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 **));
static int      color_to_val PROTO((char *));

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 */
	xonxoff_proc(preserve_start_stop);
    }

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

    if(*col > NLINE-1)
      *col = NLINE-1;
}


/*
 * 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 */
    }

    /* can't happen */
    return(0);
}


/*
 * 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(FUDGE-5))
      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 & 0x80 && Pmaster && Pmaster->hibit_entered)
      *Pmaster->hibit_entered = 1;

    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 || panicking)
      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.
 *
 * More than one "name" can map to the same "canonical_name".
 * More than one "name" can map to the same "val".
 * The "val" for a "name" and for its "canonical_name" are the same.
 */
static struct color_table *
init_color_table()
{
    struct color_table *ct = NULL, *t;
    int                 i, size, count;
    char                colorname[12];

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

	    switch(i){
	      case COL_BLACK:
		strcpy(colorname, "black");
		break;
	      case COL_RED:
		strcpy(colorname, "red");
		break;
	      case COL_GREEN:
		strcpy(colorname, "green");
		break;
	      case COL_YELLOW:
		strcpy(colorname, "yellow");
		break;
	      case COL_BLUE:
		strcpy(colorname, "blue");
		break;
	      case COL_MAGENTA:
		strcpy(colorname, "magenta");
		break;
	      case COL_CYAN:
		strcpy(colorname, "cyan");
		break;
	      case COL_WHITE:
		strcpy(colorname, "white");
		break;
	      default:
		sprintf(colorname, "color%03.3d", i);
		break;
	    }

	    t->namelen = strlen(colorname);
	    t->name = (char *)malloc((t->namelen+1) * sizeof(char));
	    if(t->name)
	      strcpy(t->name, colorname);

	    /*
	     * For an 8 color terminal, canonical black == black, but
	     * canonical red == color009, etc.
	     *
	     * This is weird. What we have is 16-color xterms where color 0
	     * is black (fine, that matches) colors 1 - 7 are called
	     * red, ..., white but they are set at 2/3rds brightness so that
	     * bold can be brighter. Those 7 colors suck. Color 8 is some
	     * sort of gray, colors 9 - 15 are real red, ..., white. On other
	     * 8 color terminals and in PC-Pine, we want to equate color 0
	     * with color 0 on 16-color, but color 1 (red) is really the
	     * same as the 16-color color 9. So color 9 - 15 are the real
	     * colors on a 16-color terminal and colors 1 - 7 are the real
	     * colors on an 8-color terminal. We make that work by mapping
	     * the 2nd eight into the first eight when displaying on an
	     * 8-color terminal, and by using the canonical_name when
	     * we modify and write out a color. The canonical name is set
	     * to the "real* color (0, 9, 10, ..., 15 on 16-color terminal).
	     */
	    if(size == count || i == 0 || i > 7){
		t->canonical_name = (char *)malloc((t->namelen+1)*sizeof(char));
		strcpy(t->canonical_name, colorname);
	    }
	    else{
		t->canonical_name = (char *)malloc(9*sizeof(char));
		sprintf(t->canonical_name, "color%03.3d", i+8);
	    }

	    t->rgb = (char *)malloc((RGBLEN+1) * sizeof(char));
	    if(t->rgb){
		if(count == 8){
		  switch(i){
		    case COL_BLACK:
		    case 8:
		      strcpy(t->rgb, "000,000,000");
		      break;
		    case COL_RED:
		    case 9:
		      strcpy(t->rgb, "255,000,000");
		      break;
		    case COL_GREEN:
		    case 10:
		      strcpy(t->rgb, "000,255,000");
		      break;
		    case COL_YELLOW:
		    case 11:
		      strcpy(t->rgb, "255,255,000");
		      break;
		    case COL_BLUE:
		    case 12:
		      strcpy(t->rgb, "000,000,255");
		      break;
		    case COL_MAGENTA:
		    case 13:
		      strcpy(t->rgb, "255,000,255");
		      break;
		    case COL_CYAN:
		    case 14:
		      strcpy(t->rgb, "000,255,255");
		      break;
		    case COL_WHITE:
		    case 15:
		      strcpy(t->rgb, "255,255,255");
		      break;
		    default:
		      /*
		       * These aren't really used as RGB values, just as
		       * strings for the lookup. We don't know how to
		       * convert to any RGB, we just know how to output
		       * the escape sequences. So it doesn't matter that
		       * the numbers in the sprintf below are too big.
		       * 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;
		  }
		}
		else if(count == 16){
		  /*
		   * This set of RGB values seems to come close to describing
		   * what a 16-color xterm gives you.
		   */
		  switch(i){
		    case COL_BLACK:
		      strcpy(t->rgb, "000,000,000");
		      break;
		    case COL_RED:		/* actually dark red */
		      strcpy(t->rgb, "174,000,000");
		      break;
		    case COL_GREEN:		/* actually dark green */
		      strcpy(t->rgb, "000,174,000");
		      break;
		    case COL_YELLOW:		/* actually dark yellow */
		      strcpy(t->rgb, "174,174,000");
		      break;
		    case COL_BLUE:		/* actually dark blue */
		      strcpy(t->rgb, "000,000,174");
		      break;
		    case COL_MAGENTA:		/* actually dark magenta */
		      strcpy(t->rgb, "174,000,174");
		      break;
		    case COL_CYAN:		/* actually dark cyan */
		      strcpy(t->rgb, "000,174,174");
		      break;
		    case COL_WHITE:		/* actually light gray */
		      strcpy(t->rgb, "174,174,174");
		      break;
		    case 8:			/* dark gray */
		      strcpy(t->rgb, "064,064,064");
		      break;
		    case 9:			/* red */
		      strcpy(t->rgb, "255,000,000");
		      break;
		    case 10:			/* green */
		      strcpy(t->rgb, "000,255,000");
		      break;
		    case 11:			/* yellow */
		      strcpy(t->rgb, "255,255,000");
		      break;
		    case 12:			/* blue */
		      strcpy(t->rgb, "000,000,255");
		      break;
		    case 13:			/* magenta */
		      strcpy(t->rgb, "255,000,255");
		      break;
		    case 14:			/* cyan */
		      strcpy(t->rgb, "000,255,255");
		      break;
		    case 15:			/* white */
		      strcpy(t->rgb, "255,255,255");
		      break;
		    default:
		      sprintf(t->rgb, "%d,%d,%d", 256+i, 256+i, 256+i);
		      break;
		  }
		}
		else{
		  switch(i){
		    case COL_BLACK:
		      strcpy(t->rgb, "000,000,000");
		      break;
		    case COL_RED:
		      strcpy(t->rgb, "255,000,000");
		      break;
		    case COL_GREEN:
		      strcpy(t->rgb, "000,255,000");
		      break;
		    case COL_YELLOW:
		      strcpy(t->rgb, "255,255,000");
		      break;
		    case COL_BLUE:
		      strcpy(t->rgb, "000,000,255");
		      break;
		    case COL_MAGENTA:
		      strcpy(t->rgb, "255,000,255");
		      break;
		    case COL_CYAN:
		      strcpy(t->rgb, "000,255,255");
		      break;
		    case COL_WHITE:
		      strcpy(t->rgb, "255,255,255");
		      break;
		    default:
		      sprintf(t->rgb, "%d,%d,%d", 256+i, 256+i, 256+i);
		      break;
		  }
		}
	    }
	}
    }

    return(ct);
}


/*
 * Map from integer color value to canonical color name.
 */
char *
colorx(color)
    int color;
{
    struct color_table *ct;
    static char         cbuf[12];

    /* before we get set up, we still need to use this a bit */
    if(!color_tbl){
	switch(color){
	  case COL_BLACK:
	    return("black");
	  case COL_RED:
	    return("red");
	  case COL_GREEN:
	    return("green");
	  case COL_YELLOW:
	    return("yellow");
	  case COL_BLUE:
	    return("blue");
	  case COL_MAGENTA:
	    return("magenta");
	  case COL_CYAN:
	    return("cyan");
	  case COL_WHITE:
	    return("white");
	  default:
	    sprintf(cbuf, "color%03.3d", color);
	    return(cbuf);
	}
    }
    
    for(ct = color_tbl; ct->name; ct++)
      if(ct->val == color)
        break;

    if(ct->name)
      return(ct->canonical_name);

    /* not supposed to get here */
    sprintf(cbuf, "color%03.3d", color);
    return(cbuf);
}


/*
 * Argument is a color name which could be an RGB string, a name like "blue",
 * or a name like "color011".
 *
 * Returns a pointer to the canonical name of the color.
 */
char *
color_to_canonical_name(s)
    char *s;
{
    struct color_table *ct;

    if(!s || !color_tbl)
      return(NULL);
    
    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)
      return(ct->canonical_name);
    
    return("");
}


/*
 * Argument is the name of a color or an RGB value that we recognize.
 * The table should be set up so that the val returned is the same whether
 * or not we choose the canonical name.
 *
 * Returns the integer value for the color.
 */
static int
color_to_val(s)
    char *s;
{
    struct color_table *ct;

    if(!s || !color_tbl)
      return(-1);
    
    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)
      return(ct->val);
    else
      return(-1);
}


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

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

	    if(t->canonical_name){
		free(t->canonical_name);
		t->canonical_name = NULL;
	    }

	    if(t->rgb){
		free(t->rgb);
		t->rgb = NULL;
	    }
	}
	
	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);

	if(the_normal_color)
	  strcpy(the_normal_color->fg, _nfcolor);
    }
    else if(the_normal_color)
      free_color_pair(&the_normal_color);
}


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);

	if(the_normal_color)
	  strcpy(the_normal_color->bg, _nbcolor);
    }
    else if(the_normal_color)
      free_color_pair(&the_normal_color);
}

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;
	    if(!panicking)
	      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(panicking)
      return;

    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(_last_fg_color){
	free(_last_fg_color);
	_last_fg_color = NULL;
    }

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

    if(the_rev_color)
      free_color_pair(&the_rev_color);

    if(the_normal_color)
      free_color_pair(&the_normal_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);
}

/*
 * Returns a pointer to the normal color.
 *
 * NOTE: Don't free this!
 */
COLOR_PAIR *
pico_get_normal_color()
{
    if(pico_usingcolor() && _nfcolor && _nbcolor &&
       pico_is_good_color(_nfcolor) && pico_is_good_color(_nbcolor)){
	if(!the_normal_color)
	  the_normal_color = new_color_pair(_nfcolor, _nbcolor);
	
	return(the_normal_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((char *) (col ? col->fg : NULL),
			   (char *) (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 || !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;
{
    int val;

    if(!s || !color_tbl)
      return(FALSE);

    if(!strcmp(s, END_PSEUDO_REVERSE)){
	EndInverse();
	return(TRUE);
    }

    if((val = color_to_val(s)) >= 0){
	/* already set correctly */
	if(!_force_fg_color_change && _last_fg_color &&
	   !strcmp(_last_fg_color,colorx(val)))
	  return(TRUE);

	_force_fg_color_change = 0;
	if(_last_fg_color){
	    free(_last_fg_color);
	    _last_fg_color = NULL;
	}
	
	if(_last_fg_color = (char *)malloc(strlen(colorx(val))+1))
	  strcpy(_last_fg_color, colorx(val));

	tfgcolor(val);
	return(TRUE);
    }
    else
      return(FALSE);
}


int
pico_set_bg_color(s)
    char *s;
{
    int val;

    if(!s || !color_tbl)
      return(FALSE);

    if(!strcmp(s, END_PSEUDO_REVERSE)){
	EndInverse();
	return(TRUE);
    }

    if((val = color_to_val(s)) >= 0){
	/* already set correctly */
	if(!_force_bg_color_change && _last_bg_color &&
	   !strcmp(_last_bg_color,colorx(val)))
	  return(TRUE);

	_force_bg_color_change = 0;
	if(_last_bg_color){
	    free(_last_bg_color);
	    _last_bg_color = NULL;
	}
	
	if(_last_bg_color = (char *)malloc(strlen(colorx(val))+1))
	  strcpy(_last_bg_color, colorx(val));

	tbgcolor(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   result[128];				/* result string */
    char   prmpt[128];
    int	   child, pid, i, done = 0, ret = 0, rv;
    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;

	    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:
		    curwp->w_flag |= WFMODE;
		    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);


    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);
    else
      vttidy();

#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 terminated abnormally (%d)",
		  WIFEXITED(stat) ? WEXITSTATUS(stat) : -1);
	  if(f)
	    ret = -1;
	  else{
	      sprintf(prmpt, "Alt editor failed, use file %.20s of size %%ld chars anyway", fn);
	      ret = -2;
	  }
	}
    }
    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(execl("/bin/sh", "sh", "-c", eb, 0) < 0)
	  exit(-1);
    }
    else {				/* error! */
	sprintf(result, "\007Can't fork %%s: %s", errstr(errno));
	ret = -1;
    }

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

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

    /*
     * Editor may have set a hibit, we don't know. Assume it did.
     */
    if(!f && Pmaster && Pmaster->hibit_entered)
     *Pmaster->hibit_entered = 1;

    /*
     * replace edited text with new text 
     */
    curbp->b_flag &= ~BFCHG;		/* make sure old text gets blasted */

    if(ret == -2){
	off_t filesize;
	char  prompt[128];

	rv = fexist(fn, "r", &filesize);
	if(rv == FIOSUC && filesize > 0){
	    sprintf(prompt, prmpt, (long) filesize);
	    /* clear bottom 3 rows */
	    pclear(term.t_nrow-2, term.t_nrow+1);
	    i = mlyesno(prompt, FALSE);
	    if(i == TRUE){
		ret = 0;
		strcpy(result, "OK, alternate %s done");
	    }
	    else
	      ret = -1;
	}
	else
	  ret = -1;
    }

    if(ret == 0)
      readin(fn, 0, 0);			/* read new text overwriting old */

    unlink(fn);				/* blast temp file */
    curbp->b_flag |= BFCHG;		/* mark dirty for packbuf() */
    ttopen();				/* reset the signals */
    pico_refresh(0, 1);			/* redraw */
    update();
    emlwrite(result, f ? "speller" : "editor");
    return(ret);
}



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
    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? */
	      pico_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);
	    pico_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);
    pico_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");
	    }
	    unlink(curbp->b_fname);
	    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;

    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((can_access(file,READ_ACCESS)==0)
		 ? (can_access(file,WRITE_ACCESS)==0)
		    ? FIOSUC
		    : FIONWT
		 : FIONRD);
	else if(!*(m+1))			/* just read access? */
	  return((can_access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD);
    }
    else if(*m == 'w' && !*(m+1))		/* write access? */
      return((can_access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT);
    else if(*m == 'x' && !*(m+1))		/* execute access? */
      return((can_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.
 *
 *        Not anymore! There are now systems that won't let you use
 *        sys_nerr and sys_errlist, so it is time to switch to using
 *        strerror instead. If this doesn't work, uncomment the old stuff.
 */
char *
errstr(err)
int err;
{
    return((err >= 0) ? (char *) strerror(err) : NULL);
#ifdef OLDWAY
    return((err >= 0 && err < sys_nerr) ? (char *)sys_errlist[err] : NULL);
#endif
}



/*
 * 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;
    size_t	   avail, alloced, incr = 1024;
    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{
#ifndef MAX
#define MAX(x,y)        ((x) > (y) ? (x) : (y))
#endif
	/*
	 * We'd like to use 512 * st_blocks as an initial estimate but
	 * some systems have a stat struct with no st_blocks in it.
	 */
	avail = alloced = MAX(sbuf.st_size, incr);
	if((sbuf.st_mode&S_IFMT) != S_IFDIR){
	    if(e)
	      sprintf(e, "\007Not a directory: \"%s\"", dn);

	    return(NULL);
	}
    }

    if((names=(char *)malloc(alloced * sizeof(char))) == 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;
	  l = strlen(p);
	  while(avail < l+1){
	      char *oldnames;

	      alloced += incr;
	      avail += incr;
	      oldnames = names;
	      if((names=(char *)realloc((void *)names, alloced * sizeof(char)))
		  == NULL){
		if(e)
		  sprintf(e, "\007Can't malloc enough space for file names");

		return(NULL);
	      }

	      np = names + (np-oldnames);
	  }

	  avail -= (l+1);

	  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;
    size_t 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-name < sizeof(name)-1;
	    *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
 */
void
fixpath(name, len)
    char  *name;
    size_t 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 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 */
}



/*
 *     Check if we can access a file in a given way
 *
 * Args: file      -- The file to check
 *       mode      -- The mode ala the access() system call, see ACCESS_EXISTS
 *                    and friends in pine.h.
 *
 * Result: returns 0 if the user can access the file according to the mode,
 *         -1 if he can't (and errno is set).
 */
int
can_access(file, mode)
    char *file;
    int   mode;
{
    return(access(file, mode));
}


/*
 * This routine is derived from BSD4.3 code,
 * Copyright (c) 1987 Regents of the University of California.
 * All rights reserved.
 */
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)mktemp.c	5.7 (Berkeley) 6/27/88";
#endif /* LIBC_SCCS and not lint */

static char *
was_nonexistent_tmp_name(as, create_it, ext)
    char *as;
    int   create_it;
    char *ext;
{
    register char  *start, *trv;
    struct stat     sbuf;
    unsigned        pid;
    static unsigned n = 0;
    int             fd, tries = 0;

    pid = ((unsigned)getpid() * 100) + n++;

    /* extra X's get set to 0's */
    for(trv = as; *trv; ++trv)
      ;

    /*
     * We should probably make the name random instead of having it
     * be the pid.
     */
    while(*--trv == 'X'){
	*trv = (pid % 10) + '0';
	pid /= 10;
    }

    /* add the extension, enough room guaranteed by caller */
    if(ext){
	strcat(as, ".");
	strcat(as, ext);
    }

    /*
     * 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((char *)NULL);

	*trv = '/';
    }
    else if (stat(".", &sbuf) == -1)
      return((char *)NULL);

    for(;;){
	/*
	 * Check with lstat to be sure we don't have
	 * a symlink. If lstat fails and no such file, then we
	 * have a winner. Otherwise, lstat shouldn't fail.
	 * If lstat succeeds, then skip it because it exists.
	 */
	if(lstat(as, &sbuf)){		/* lstat failed */
	    if(errno == ENOENT){		/* no such file, success */
		/*
		 * If create_it is set, create the file so that the
		 * evil ones don't have a chance to put something there
		 * that they can read or write before we create it
		 * ourselves.
		 */
		if(!create_it ||
		   ((fd=open(as, O_CREAT|O_EXCL|O_WRONLY,0600)) >= 0
		    && close(fd) == 0))
		  return(as);
		else if(++tries > 3)		/* open failed unexpectedly */
		  return((char *)NULL);
	    }
	    else				/* failed for unknown reason */
	      return((char *)NULL);
	}

	for(trv = start;;){
	    if(!*trv)
	      return((char *)NULL);

	    /*
	     * Change the digits from the initial values into
	     * lower case letters and try again.
	     */
	    if(*trv == 'z')
	      *trv++ = 'a';
	    else{
		if(isdigit((unsigned char)*trv))
		  *trv = 'a';
		else
		  ++*trv;

		break;
	    }
	}
    }
    /*NOTREACHED*/
}


/*
 * 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 similar.
      We want it to use the TMPDIR/TMP/TEMP environment variable only if dir
      is NULL, instead of using it regardless if it is set.
      We also want it to be safer than tempnam().
      If we return a filename, we are saying that the file did not exist
      at the time this function was called (and it wasn't a symlink pointing
      to a file that didn't exist, either).
      If dir is NULL this is a temp file in a public directory. In that
      case we create the file with permission 0600 before returning.

  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;
    size_t      l, ll;
    char       *f, *name;

    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 &&
			 !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TEMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(dir && !stat(dir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
	                 !can_access(dir, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, dir, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

#ifndef P_tmpdir
#define	P_tmpdir	"/usr/tmp"
#endif
    if(!stat(P_tmpdir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(P_tmpdir, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, P_tmpdir, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!stat("/tmp", &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access("/tmp", WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, "/tmp", NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    free((void *)name);
    return((char *)NULL);

done:
    if(name[0] && *((f = &name[l=strlen(name)]) - 1) != '/' && l+1 < NFILEN){
	*f++ = '/';
	*f = '\0';
	l++;
    }

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

    if(l+6 < NFILEN)
      strcpy(f, "XXXXXX");
    else{
	free((void *)name);
	return((char *)NULL);
    }

    return(was_nonexistent_tmp_name(name, 1, NULL));
}

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

     Like temp_nam but create a unique name with an extension.
 
 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_ext(dir, prefix, ext)
    char *dir, *prefix, *ext;
{
    struct stat buf;
    size_t      l, ll;
    char       *f, *name;

    if(ext == NULL || *ext == '\0')
      return(temp_nam(dir, prefix));
    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 &&
			 !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TEMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(dir && !stat(dir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
	                 !can_access(dir, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, dir, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

#ifndef P_tmpdir
#define	P_tmpdir	"/usr/tmp"
#endif
    if(!stat(P_tmpdir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(P_tmpdir, WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, P_tmpdir, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!stat("/tmp", &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access("/tmp", WRITE_ACCESS|EXECUTE_ACCESS)){
	strncpy(name, "/tmp", NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    free((void *)name);
    return((char *)NULL);

done:
    if(name[0] && *((f = &name[l=strlen(name)]) - 1) != '/' && l+1 < NFILEN){
	*f++ = '/';
	*f = '\0';
	l++;
    }

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

    if(l+6+strlen(ext)+1 < NFILEN)
      strcpy(f, "XXXXXX");
    else{
	free((void *)name);
	return((char *)NULL);
    }

    return(was_nonexistent_tmp_name(name, 1, ext));
}


/*
 * tmpname - return a temporary file name in the given buffer, the filename
 * is in the directory dir unless dir is NULL. The file did not exist at
 * the time of the temp_nam call, but was created by temp_nam.
 */
void
tmpname(dir, name)
char *dir;
char *name;
{
    char *t;

    if(t = temp_nam((dir && *dir) ? dir : NULL, "pico.")){
	strncpy(name, t, NFILEN-1);
	name[NFILEN-1] = '\0';
	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
 *
 */
void
P_close(fp)
FILE *fp;
{
    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"))
      /* fix unsafe sprintf - noticed by petter wahlman <petter@bluezone.no> */
      sprintf(inbox, "%.*s", sizeof(inbox)-1, p);
    else
      sprintf(inbox,"%.*s/%.*s", sizeof(inbox)/3, MAILDIR, sizeof(inbox)/3,
	      (char *) 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 > below.
 *                 Newmail won't necessarily check, but we want to give it
 *                 a chance to check or do a keepalive.
 */
time_to_check()
{
    static time_t lasttime = 0L;

    if(!timeo)
      return(FALSE);

    if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : 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
 */
void
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(collation, ctype)
    int collation;
    int ctype;
{
    extern int collator();  /* strcoll isn't declared on all systems */
#ifdef LC_COLLATE
    char *status = NULL;
#endif

    pcollator = strucmp;

#ifdef LC_COLLATE
  if(collation){
    /*
     * 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
  if(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;
    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   col_offset, 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 */


