int timeo = 0; int (*pcollator)(); #if defined(sv3) || defined(ct) /* * Windowing structure to support JWINSIZE/TIOCSWINSZ/TIOCGWINSZ */ #define ENAMETOOLONG 78 struct winsize { unsigned short ws_row; /* rows, in characters*/ unsigned short ws_col; /* columns, in character */ unsigned short ws_xpixel; /* horizontal size, pixels */ unsigned short ws_ypixel; /* vertical size, pixels */ }; #endif #ifdef SIGCHLD static jmp_buf pico_child_state; static short pico_child_jmp_ok, pico_child_done; #endif #ifdef MOUSE static int mexist = 0; /* is the mouse driver installed? */ static unsigned mnoop; #endif struct color_table { char *name; 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); } if(!Pmaster || (gmode ^ MDEXTFB)) picosigs(); return(1); } /* * ttresize - recompute the screen dimensions if necessary, and then * adjust pico's internal buffers accordingly. */ void ttresize () { int row = -1, col = -1; ttgetwinsz(&row, &col); resize_pico(row, col); } /* * picosigs - Install any handlers for the signals we're interested * in catching. */ 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(timeo)) return(NODATA); switch(status = kbseq(simple_ttgetc, NULL, bail, &ch)){ case 0: /* regular character */ break; case KEY_DOUBLE_ESC: /* * Special hack to get around comm devices eating control characters. */ if(!ReadyForKey(5)) return(BADESC); /* user typed ESC ESC, then stopped */ else ch = (*term.t_getchar)(NODATA, NULL, bail); ch &= 0x7f; if(isdigit((unsigned char)ch)){ int n = 0, i = ch - '0'; if(i < 0 || i > 2) return(BADESC); /* bogus literal char value */ while(n++ < 2){ if(!ReadyForKey(5) || (!isdigit((unsigned char) (ch = (*term.t_getchar)(NODATA, NULL, bail))) || (n == 1 && i == 2 && ch > '5') || (n == 2 && i == 25 && ch > '5'))){ return(BADESC); } i = (i * 10) + (ch - '0'); } ch = i; } else{ if(islower((unsigned char)ch)) /* canonicalize if alpha */ ch = toupper((unsigned char)ch); return((isalpha((unsigned char)ch) || ch == '@' || (ch >= '[' && ch <= '_')) ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch)); } break; #ifdef MOUSE case KEY_XTERM_MOUSE: { /* * Special hack to get mouse events from an xterm. * Get the details, then pass it past the keymenu event * handler, and then to the installed handler if there * is one... */ static int down = 0; int x, y, button; unsigned cmd; button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03; x = (*term.t_getchar)(NODATA, NULL, bail) - '!'; y = (*term.t_getchar)(NODATA, NULL, bail) - '!'; if(button == 0){ down = 1; if(checkmouse(&cmd, 1, x, y)) return((int)cmd); } else if(down && button == 3){ down = 0; if(checkmouse(&cmd, 0, x, y)) return((int)cmd); } return(NODATA); } break; #endif /* MOUSE */ case KEY_UP : case KEY_DOWN : case KEY_RIGHT : case KEY_LEFT : case KEY_PGUP : case KEY_PGDN : case KEY_HOME : case KEY_END : case KEY_DEL : case F1 : case F2 : case F3 : case F4 : case F5 : case F6 : case F7 : case F8 : case F9 : case F10 : case F11 : case F12 : return(status); case KEY_SWALLOW_Z: status = BADESC; case KEY_SWAL_UP: case KEY_SWAL_DOWN: case KEY_SWAL_LEFT: case KEY_SWAL_RIGHT: do if(!ReadyForKey(2)){ status = BADESC; break; } while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail))); return((status == BADESC) ? status : status - (KEY_SWAL_UP - KEY_UP)); break; case KEY_KERMIT: do{ cc = ch; if(!ReadyForKey(2)) return(BADESC); else ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f; }while(cc != '\033' && ch != '\\'); ch = NODATA; break; case BADESC: (*term.t_beep)(); return(status); default: /* punt the whole thing */ (*term.t_beep)(); break; } if(ch & 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) 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; free_color_table(&color_tbl); if(ANSI_COLOR()) putpad("\033[39;49m"); else{ if(_op) putpad(_op); if(_oc) putpad(_oc); } } } } unsigned pico_get_color_options() { return(color_options); } /* * Absolute set of options. Caller has to OR things together and so forth. */ void pico_set_color_options(flags) unsigned flags; { color_options = flags; } void pico_endcolor() { pico_toggle_color(0); if(_nfcolor){ free(_nfcolor); _nfcolor = NULL; } if(_nbcolor){ free(_nbcolor); _nbcolor = NULL; } if(_rfcolor){ free(_rfcolor); _rfcolor = NULL; } if(_rbcolor){ free(_rbcolor); _rbcolor = NULL; } if(_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(col ? col->fg : NULL, col ? col->bg : NULL, flags)); } /* * Sets color to (fg,bg). * Flags == PSC_NONE No alternate default if fg,bg fails. * == PSC_NORM Set it to Normal color on failure. * == PSC_REV Set it to Reverse color on failure. * * If flag PSC_RET is set, returns an allocated copy of the previous * color pair, otherwise returns NULL. */ COLOR_PAIR * pico_set_colors(fg, bg, flags) char *fg, *bg; int flags; { int uc; COLOR_PAIR *cp = NULL, *rev = NULL; if(flags & PSC_RET) cp = pico_get_cur_color(); if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){ EndInverse(); if(cp) free_color_pair(&cp); } else if(!((uc=pico_usingcolor()) && fg && bg && pico_set_fg_color(fg) && pico_set_bg_color(bg))){ if(uc && flags & PSC_NORM) pico_set_normal_color(); else if(flags & PSC_REV){ if(rev = pico_get_rev_color()){ pico_set_fg_color(rev->fg); /* these will succeed */ pico_set_bg_color(rev->bg); } else{ StartInverse(); if(cp){ strcpy(cp->fg, END_PSEUDO_REVERSE); strcpy(cp->bg, END_PSEUDO_REVERSE); } } } } return(cp); } int pico_is_good_color(s) char *s; { struct color_table *ct; if(!s || !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 *cp; char *args[MAXARGS]; /* ptrs into edit command */ 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); cp = eb; for(i=0; *cp != '\0';i++){ /* build args array */ if(i < MAXARGS){ args[i] = NULL; /* in case we break out */ } else{ emlwrite("Too many args for command!", NULL); return(-1); } while(isspace((unsigned char)(*cp))) if(*cp != '\0') cp++; else break; args[i] = cp; while(!isspace((unsigned char)(*cp))) if(*cp != '\0') cp++; else break; if(*cp != '\0') *cp++ = '\0'; } args[i] = NULL; for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){ movecursor(i, 0); if(!i){ fputs("Invoking alternate ", stdout); fputs(f ? "speller..." : "editor...", stdout); } peeol(); } (*term.t_flush)(); if(Pmaster) (*Pmaster->tty_fix)(0); 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(execvp(args[0], args) < 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. */ char * errstr(err) int err; { return((err >= 0 && err < sys_nerr) ? (char *)sys_errlist[err] : NULL); } /* * getfnames - return all file names in the given directory in a single * malloc'd string. n contains the number of names */ char * getfnames(dn, pat, n, e) char *dn, *pat, *e; int *n; { long l; 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{ #define MAX(x,y) ((x) > (y) ? (x) : (y)) /* * 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) char *as; int create_it; { register char *start, *trv; struct stat sbuf; unsigned pid; static unsigned n = 0; int fd; 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; } /* * 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 /* 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)); } /* * 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 */ sprintf(inbox, "%.*s", sizeof(inbox)-1, p); else sprintf(inbox,"%.*s/%.*s", sizeof(inbox)/3, MAILDIR, sizeof(inbox)/3, getlogin()); if(stat(inbox, &sbuf) == 0){ ret = sbuf.st_atime <= sbuf.st_mtime && (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime); lastchk = sbuf.st_mtime; return(ret); } else return(ret); } /* * time_to_check - checks the current time against the last time called * and returns true if the elapsed time is > timeo */ time_to_check() { static time_t lasttime = 0L; if(!timeo) return(FALSE); if(time((time_t *) 0) - lasttime > (time_t)timeo){ lasttime = time((time_t *) 0); return(TRUE); } else return(FALSE); } /* * sstrcasecmp - compare two pointers to strings case independently */ int sstrcasecmp(s1, s2) const QSType *s1, *s2; { return((*pcollator)(*(char **)s1, *(char **)s2)); } /*-------------------------------------------------- A case insensitive strcmp() Args: o, r -- The two strings to compare Result: integer indicating which is greater ---*/ int strucmp(o, r) register char *o, *r; { if(o == NULL){ if(r == NULL) return 0; else return -1; } else if(r == NULL) return 1; while(*o && *r && ((isupper((unsigned char)(*o)) ? (unsigned char)tolower((unsigned char)(*o)) : (unsigned char)(*o)) == (isupper((unsigned char)(*r)) ? (unsigned char)tolower((unsigned char)(*r)) : (unsigned char)(*r)))){ o++; r++; } return((isupper((unsigned char)(*o)) ? tolower((unsigned char)(*o)) : (int)(unsigned char)(*o)) - (isupper((unsigned char)(*r)) ? tolower((unsigned char)(*r)) : (int)(unsigned char)(*r))); } /*---------------------------------------------------------------------- A case insensitive strncmp() Args: o, r -- The two strings to compare n -- length to stop comparing strings at Result: integer indicating which is greater ----*/ int struncmp(o, r, n) register char *o, *r; register int n; { if(n < 1) return 0; if(o == NULL){ if(r == NULL) return 0; else return -1; } else if(r == NULL) return 1; n--; while(n && *o && *r && ((isupper((unsigned char)(*o)) ? (unsigned char)tolower((unsigned char)(*o)) : (unsigned char)(*o)) == (isupper((unsigned char)(*r)) ? (unsigned char)tolower((unsigned char)(*r)) : (unsigned char)(*r)))){ o++; r++; n--; } return((isupper((unsigned char)(*o)) ? tolower((unsigned char)(*o)) : (int)(unsigned char)(*o)) - (isupper((unsigned char)(*r)) ? tolower((unsigned char)(*r)) : (int)(unsigned char)(*r))); } /* * chkptinit -- initialize anything we need to support composer * checkpointing */ 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 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 */