4171 lines
89 KiB
Plaintext
Executable File
4171 lines
89 KiB
Plaintext
Executable File
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 */
|
|
|
|
|