home *** CD-ROM | disk | FTP | other *** search
- #if !defined(lint) && !defined(DOS)
- static char rcsid[] = "$Id: os_unix.c,v 4.37 1993/11/23 05:40:11 mikes Exp $";
- #endif
- /*
- * Program: Operating system dependent routines - Ultrix 4.1
- *
- *
- * Michael Seibel
- * Networks and Distributed Computing
- * Computing and Communications
- * University of Washington
- * Administration Builiding, AG-44
- * Seattle, Washington, 98195, USA
- * Internet: mikes@cac.washington.edu
- *
- * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
- *
- * Copyright 1991-1993 University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee to the University of
- * Washington is hereby granted, provided that the above copyright notice
- * appears in all copies and that both the above copyright notice and this
- * permission notice appear in supporting documentation, and that the name
- * of the University of Washington not be used in advertising or publicity
- * pertaining to distribution of the software without specific, written
- * prior permission. This software is made available "as is", and
- * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
- * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
- * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
- * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Pine and Pico are trademarks of the University of Washington.
- * No commercial use of these trademarks may be made without prior
- * written permission of the University of Washington.
- *
- *
- * Notes:
- *
- * - SGI IRIX 4.0.1 port by:
- * johnb@edge.cis.mcmaster.ca, 2 April 1992
- *
- * - Dynix/PTX port by:
- * Donn Cave, UCS/UW, 15 April 1992
- *
- * - 3B2, 3b1/7300, SCO ports by:
- * rll@felton.felton.ca.us, 7 Feb. 1993
- *
- * - Probably have to break this up into separate os_type.c files since
- * the #ifdef's are getting a bit cumbersome.
- *
- */
-
- #include <stdio.h>
- #include <errno.h>
- #include <setjmp.h>
- #include <time.h>
- #include <pwd.h>
- #if defined(sv4) || defined(ptx)
- #include <stropts.h>
- #include <poll.h>
- #endif
- #if defined(POSIX)
- #include <termios.h>
- #if defined(a32) || defined(cvx)
- #include <sys/ioctl.h>
- #endif
- #else
- #if defined(sv3) || defined(sgi) || defined(isc) || defined(ct)
- #include <termio.h>
- #if defined(isc)
- #include <sys/sioctl.h>
- #endif
- #else
- #include <sgtty.h>
- #endif /* sv3 || sgi || isc */
- #endif /* POSIX */
-
- #include "osdep.h"
- #include "pico.h"
- #include "estruct.h"
- #include "edef.h"
- #include "efunc.h"
- #include <fcntl.h>
- #include <sys/wait.h>
- #include <sys/file.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #if defined(a32)
- #include <sys/select.h>
- #endif
-
-
- /*
- * Immediately below are includes and declarations for the 3 basic
- * terminal drivers supported; POSIX, SysVR3, and BSD
- */
- #ifdef POSIX
-
- struct termios nstate,
- ostate;
- #else
- #if defined(sv3) || defined(sgi) || defined(isc)
-
- struct termio nstate,
- ostate;
-
- #else
- struct sgttyb ostate; /* saved tty state */
- struct sgttyb nstate; /* values for editor mode */
- struct ltchars oltchars; /* old term special chars */
- struct ltchars nltchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- struct tchars otchars; /* old term special chars */
- struct tchars ntchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
- #endif /* sv3 || sgi || isc */
- #endif /* POSIX */
-
- #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 bsd
- int errno; /* ya, I know... */
- #endif
-
- #if (defined(bsd) || defined(dyn) || defined(ct)) && !defined(LINUX)
- #define SIGTYPE int
- #else
- #define SIGTYPE void
- #endif
-
-
- #ifdef ANSI
- int kbseq(int *);
- SIGTYPE do_hup_signal();
- SIGTYPE rtfrmshell();
- #ifdef TIOCGWINSZ
- SIGTYPE winch_handler();
- #endif
- #else
- int kbseq();
- SIGTYPE do_hup_signal();
- SIGTYPE rtfrmshell();
- #ifdef TIOCGWINSZ
- SIGTYPE winch_handler();
- #endif
- #endif
-
-
- /*
- * for alt_editor arg[] building
- */
- #define MAXARGS 10
- #ifdef HEBREW
- #include "hebrew.h"
-
- unsigned char xlat_tab[]={
- 0xf9, /* a - shin*/
- 0xf0, /* b - nun */
- 0xe1, /* c - bet */
- 0xe2, /* d - gimel*/
- 0xf7, /* e - kuf*/
- 0xeb, /* f - chaf */
- 0xf2, /* g - ayin */
- 0xe9, /* h - yud */
- 0xef, /* i - nun sofit */
- 0xe7, /* j - het */
- 0xec, /* k - lamed */
- 0xea, /* l - chaf sofit */
- 0xf6, /* m - zadik */
- 0xee, /* n - mem */
- 0xed, /* o - mem sofit */
- 0xf4, /* p - pey */
- 0x2f, /* q - / */
- 0xf8, /* r - raish */
- 0xe3, /* s - dalet */
- 0xe0, /* t - aleph */
- 0xe5, /* u - vav */
- 0xe4, /* v - hey */
- 0x27, /* w - ' */
- 0xf1, /* x - samech */
- 0xe8, /* y - tet */
- 0xe6, /* z - zayin */
- };
- unsigned char xlat_str1[]="`;',./";
- unsigned char xlat_str2[]={
- ';', /* ` - ; */
- 0xf3, /* ; - pey sofit */
- ',', /* ' - , */
- 0xfa, /* , - tav */
- 0xf5, /* . - zadik sofit */
- '.', /* / - . */
- };
- #endif
-
- /*
- * 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){
- #ifdef POSIX
- tcgetattr (0, &ostate);
- tcgetattr (0, &nstate);
- nstate.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
- nstate.c_iflag &= ~ICRNL;
- nstate.c_oflag &= ~(ONLCR | OPOST);
- nstate.c_cc[VMIN] = 1;
- nstate.c_cc[VTIME] = 0;
- tcsetattr (0, TCSADRAIN, &nstate);
- #else
- #if defined(sv3) || defined(sgi) || defined(isc) || defined(ct)
- (void) ioctl(0, TCGETA, &ostate);
- (void) ioctl(0, TCGETA, &nstate); /** again! **/
-
- nstate.c_lflag &= ~(ICANON | ISIG | ECHO); /* noecho raw mode */
- nstate.c_oflag &= ~(OPOST | ONLCR);
- nstate.c_iflag &= ~ICRNL;
-
- nstate.c_cc[VMIN] = '\01'; /* minimum # of chars to queue */
- nstate.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
- (void) ioctl(0, TCSETA, &nstate);
- #else
- ioctl(0, TIOCGETP, &ostate); /* save old state */
- ioctl(0, TIOCGLTC, &oltchars); /* Save old lcharacters */
- ioctl(0, TIOCGETC, &otchars); /* Save old characters */
- ioctl(0, TIOCGETP, &nstate); /* get base of new state */
- nstate.sg_flags |= RAW;
- nstate.sg_flags &= ~(ECHO|CRMOD); /* no echo for now... */
- ioctl(0, TIOCSETP, &nstate); /* set mode */
-
- ioctl(0, TIOCSLTC, &nltchars); /* put new lcharacter into K */
- ioctl(0, TIOCSETC, &ntchars); /* put new character into K */
- #endif /* sv3 */
- #endif /* POSIX */
- }
-
- signal(SIGHUP, do_hup_signal); /* deal with SIGHUP */
- signal(SIGTERM, do_hup_signal); /* deal with SIGTERM */
- #ifdef SIGTSTP
- signal(SIGTSTP, SIG_DFL);
- #endif
- #ifdef TIOCGWINSZ
- signal(SIGWINCH, winch_handler); /* window size changes */
- #endif
- return(1);
- }
-
-
-
- /*
- * 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 TIOCGWINSZ
- signal(SIGWINCH, SIG_DFL);
- #endif
- }
- else{
- #ifdef POSIX
- tcsetattr (0, TCSADRAIN, &ostate);
- #else
- #if defined(sv3) || defined(sgi) || defined(isc) || defined(ct)
- ioctl(0, TCSETA, &ostate);
- #else
- ioctl(0, TIOCSETP, &ostate);
- ioctl(0, TIOCSLTC, &oltchars);
- ioctl(0, TIOCSETC, &otchars);
-
- /*
- * This works around a really weird problem. On slow speed lines,
- * if an exit happens with some number of characters still to be
- * written in the terminal driver, one or more characters will
- * be changed when they finally get drained. This can be reproduced
- * on a 2400bps line, writing a multi-line buffer on exit using
- * a vt100 type terminal. It turns out the last char in the
- * escape sequence turning off reverse video was getting changed
- * from 'm' to ' '. I said it was weird.
- */
- if(ostate.sg_ospeed <= B2400)
- sleep(1);
- #endif /* sv3 || sgi || isc */
- #endif /* POSIX */
- }
-
- return(1);
- }
-
-
- /*
- * ttspeed - return TRUE if tty line speed < 9600 else return FALSE
- */
- ttisslow()
- {
- #if defined(POSIX)
- struct termios tty;
-
- return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
- #else
- #if defined(sv3) || defined(sgi) || defined(isc)
- struct termio tty;
-
- return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
- #else
- struct sgttyb tty;
-
- return((ioctl(1, TIOCGETP, &tty) == 0) ? tty.sg_ospeed < B9600 : FALSE);
- #endif
- #endif
- }
-
-
- /*
- * ttgetwinsz - set global rows and columns values and return
- */
- ttgetwinsz()
- {
- #ifdef TIOCGWINSZ
- struct winsize win;
-
- if (ioctl(0, TIOCGWINSZ, &win) == 0) {
- term.t_ncol = (win.ws_col) ? win.ws_col : 80;
- term.t_nrow = (win.ws_row) ? win.ws_row - 1 : 23;
- }
- #endif
- }
-
-
- /*
- * ttputc - Write a character to the display.
- */
- #ifndef HEBREW
- ttputc(c)
- {
- return putc(c, stdout);
- }
- #else
- ttputc(c)
- {
- extern int ttrow,ttcol;
- int ret;
-
- if(compose_heb && hebmode){
- ret=stdputc(c);
- (*term.t_move)(ttrow, ttcol+1);
- }
- else ret=stdputc(c);
- return ret;
- }
-
- stdputc(c)
- {
- return (putc(c, stdout));
- }
- #endif
-
-
- /*
- * 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.
- */
- ttgetc()
- {
- unsigned char c;
- int i;
-
- if((i = read(0, &c, 1)) <= 0){
- if(i == 0 || errno == EINTR)
- return(NODATA);
- else
- kill(getpid(), SIGHUP);
- }
- else
- return((int)c);
- }
-
-
- #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
-
-
- /*
- * 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 c;
- #ifdef HEBREW
- unsigned char *ptr;
- #endif
-
- if(timeout){
- /*
- * simple use of select/poll here to handle requested timeouts
- * while waiting for keyboard input...
- */
- #if defined(ptx) || defined(sv4)
- struct pollfd pollfd;
- int rv;
-
- pollfd.fd = 0;
- pollfd.events = POLLIN;
- while((rv = poll(&pollfd, 1, timeout * 1000)) < 0 && errno == EAGAIN)
- ;
- #else
- struct timeval ts;
- fd_set readfds;
- int rv;
-
- FD_ZERO(&readfds); /* blank out all bits */
- FD_SET(0, &readfds); /* set stdin's bit */
- ts.tv_sec = timeout; /* set the timeout */
- ts.tv_usec = 0;
-
- rv = select(1, &readfds, 0, &readfds, &ts); /* read stdin */
- #endif
- if(rv < 0){
- if(errno == EINTR) /* interrupted? */
- return(NODATA); /* return like we timed out */
- else
- return(NODATA); /* BUG: should bomb out? */
- }
- else if(rv == 0)
- return(NODATA); /* we really did time out */
- }
-
- if ((c = (*term.t_getchar)()) == METACH) { /* Apply M- prefix */
- int status;
-
- /*
- * this code should intercept special keypad keys
- */
- switch(status = kbseq(&c)){
- case 0 : /* no dice */
- return(c);
- case K_PAD_UP :
- case K_PAD_DOWN :
- case K_PAD_RIGHT :
- case K_PAD_LEFT :
- case K_PAD_PREVPAGE :
- case K_PAD_NEXTPAGE :
- case K_PAD_HOME :
- return(status);
- case F1 :
- case F2 :
- case F3 :
- case F4 :
- case F5 :
- case F6 :
- case F7 :
- case F8 :
- case F9 :
- case F10 :
- case F11 :
- case F12 :
- return(status);
- case BADESC :
- if(c == '\033'){
- c = (*term.t_getchar)();
- if(islower(c)) /* canonicalize c */
- c = toupper(c);
-
- return((isalpha(c) || c == '@' || (c >= '[' && c <= '_'))
- ? (CTRL | c) : c);
- }
-
- default : /* punt the whole thing */
- (*term.t_beep)();
- break;
- }
- }
-
- if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
- c = CTRL | (c+'@');
- #ifdef HEBREW
- if(compose_heb && (!message_mode || search_mode) && (hebmain!=eng_in_heb)){
- if(c >= 'a' && c <= 'z')c = xlat_tab[c-'a'];
- else{
- ptr = (unsigned char *)strchr(xlat_str1,c);
- if(ptr!=NULL)c = xlat_str2[(int)(ptr-xlat_str1)];
- }
- }
- #endif
- return (c);
-
- }
-
-
-
- /*
- * kbseq - looks at an escape sequence coming from the keyboard and
- * compares it to a trie of known keyboard escape sequences, and
- * performs the function bound to the escape sequence.
- *
- * returns: BADESC, the escaped function, or 0 if not found.
- */
- kbseq(c)
- int *c;
- {
- register char b;
- register int first = 1;
- register struct KBSTREE *current = kpadseqs;
- #ifdef HEBREW
- register int firsth = 1;
- #endif
-
- if(kpadseqs == NULL) /* bag it */
- return(BADESC);
-
- while(1){
- *c = b = (*term.t_getchar)();
- #ifdef HEBREW
- if(firsth){
- firsth=0;
- if(strchr(escfuns,b&0xffdf)){
- *c &= 0xffdf;
- *c |= META;
- return 0;
- }
- }
- #endif
-
- while(current->value != b){
- if(current->left == NULL){ /* NO MATCH */
- if(first)
- return(BADESC);
- else
- return(0);
- }
- current = current->left;
- }
-
- if(current->down == NULL) /* match!!!*/
- return(current->func);
- else
- current = current->down;
-
- first = 0;
- }
- }
-
-
-
- /*
- * 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 *writetmp();
- int child, pid, i, done = 0;
- long l;
- #if defined(POSIX) || defined(sv3) || defined(COHERENT) || defined(isc)
- int stat;
- #else
- union wait stat;
- #endif
- FILE *p;
- SIGTYPE (*ohup)(), (*oint)(), (*osize)(), (*ostop)(), (*ostart)();
-
- if(Pmaster == NULL)
- return;
-
- if(gmode&MDSCUR){
- emlwrite("Alternate editor not available in restricted mode", NULL);
- return;
- }
-
- if(Pmaster->alt_ed == NULL){
- if(!(gmode&MDADVN)){
- emlwrite("\007Unknown Command",NULL);
- return;
- }
-
- if(getenv("EDITOR"))
- strcpy(eb, (char *)getenv("EDITOR"));
- else
- *eb = '\0';
-
- while(!done){
- pid = mlreplyd("Which alternate editor ? ", eb, NLINE, QDEFLT);
-
- switch(pid){
- case ABORT:
- return(-1);
- case HELPCH:
- emlwrite("no alternate editor help yet", NULL);
-
- /* take sleep and break out after there's help */
- sleep(3);
- break;
- case (CTRL|'L'):
- sgarbf = TRUE;
- update();
- break;
- case TRUE:
- case FALSE: /* does editor exist ? */
- if(*eb == '\0'){ /* leave silently? */
- mlerase();
- return(-1);
- }
-
- done++;
- break;
- default:
- break;
- }
- }
- }
- else
- strcpy(eb, Pmaster->alt_ed);
-
- if((fn=writetmp(0, 1)) == 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(*cp))
- if(*cp != '\0')
- cp++;
- else
- break;
-
- args[i] = cp;
-
- while(!isspace(*cp))
- if(*cp != '\0')
- cp++;
- else
- break;
-
- if(*cp != '\0')
- *cp++ = '\0';
- }
-
- args[i] = NULL;
-
- if(Pmaster)
- (*Pmaster->raw_io)(0); /* turn OFF raw mode */
-
- if(child=fork()){ /* wait for the child to finish */
- ohup = signal(SIGHUP, SIG_IGN); /* ignore signals for now */
- oint = signal(SIGINT, SIG_IGN);
- #ifdef TIOCGWINSZ
- osize = signal(SIGWINCH, SIG_IGN);
- #endif
-
- /*
- * BUG - wait should be made non-blocking and mail_pings or something
- * need to be done in the loop to keep the imap stream alive
- */
- while((pid=(int)wait(&stat)) != child)
- ;
-
- signal(SIGHUP, ohup); /* restore signals */
- signal(SIGINT, oint);
- #ifdef TIOCGWINSZ
- signal(SIGWINCH, osize);
- #endif
- }
- else{ /* spawn editor */
- signal(SIGHUP, SIG_DFL); /* let editor handle signals */
- signal(SIGINT, SIG_DFL);
- #ifdef TIOCGWINSZ
- signal(SIGWINCH, SIG_DFL);
- #endif
- if(execvp(args[0], args) < 0)
- exit(1);
- }
-
- if(Pmaster)
- (*Pmaster->raw_io)(1); /* turn ON raw mode */
-
- /*
- * replace edited text with new text
- */
- curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */
- readin(fn, 0); /* read new text overwriting old */
- unlink(fn); /* blast temp file */
- curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */
- ttopen(); /* reset the signals */
- refresh(0, 1); /* redraw */
- }
-
-
-
- /*
- * bktoshell - suspend and wait to be woken up
- */
- bktoshell() /* suspend MicroEMACS and wait to wake up */
- {
- #ifdef SIGTSTP
- int pid;
-
- if(!(gmode&MDSSPD)){
- emlwrite("\007Unknown command: ^Z", NULL);
- return;
- }
-
- if(Pmaster){
- (*Pmaster->raw_io)(0); /* actually in pine source */
-
- movecursor(term.t_nrow, 0);
- printf("\n\n\nUse \"fg\" to return to Pine\n");
-
- }
- else
- vttidy();
-
- 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);
- #endif
- }
-
-
- /*
- * rtfrmshell - back from shell, fix modes and return
- */
- SIGTYPE
- rtfrmshell()
- {
- #ifdef SIGCONT
- signal(SIGCONT, SIG_DFL);
-
- if(Pmaster){
- (*Pmaster->raw_io)(1); /* actually in pine source */
- (*Pmaster->keybinit)(gmode&MDFKEY); /* using f-keys? */
- }
-
- ttopen();
-
- #ifdef TIOCGWINSZ
- {
- struct winsize win;
- extern int resize_pico();
-
- /*
- * refit pico of window size changed....
- */
- if (ioctl(0, TIOCGWINSZ, &win) == 0) {
- if (win.ws_col && win.ws_row)
- resize_pico(win.ws_row - 1, win.ws_col);
- }
- }
- #endif
-
- sgarbf = TRUE;
- curwp->w_flag = WFHARD;
- refresh(0, 1);
- #endif
- }
-
-
-
- /*
- * do_hup_signal - jump back in the stack to where we can handle this
- */
- SIGTYPE
- do_hup_signal()
- {
- if(Pmaster){
- extern jmp_buf finstate;
-
- signal(SIGHUP, SIG_IGN); /* don't bother us. */
- signal(SIGTERM, SIG_IGN);
- longjmp(finstate, COMP_GOTHUP);
- }
- else{
- /*
- * if we've been interrupted and the buffer is changed,
- * save it...
- */
- if(anycb() == TRUE){ /* time to save */
- if(curbp->b_fname[0] == '\0'){ /* name it */
- strcpy(curbp->b_fname, "pico.save");
- }
- else{
- strcat(curbp->b_fname, ".save");
- }
- writeout(curbp->b_fname);
- }
- 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 - ^_ */
- 0, 0x17, /* SP - ' , ( - / */
- 0xff, 0xc0, /* 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 or rw */
- long *l;
- {
- struct stat sbuf;
-
- if(l)
- *l = 0L;
-
- if(stat(file, &sbuf) < 0){
- switch(errno){
- case ENOENT : /* File not found */
- return(FIOFNF);
- #ifdef ENAMETOOLONG
- case ENAMETOOLONG : /* Name is too long */
- return(FIOLNG);
- #endif
- default: /* Some other error */
- return(FIOERR);
- }
- }
-
- if(l)
- *l = sbuf.st_size;
-
- if((sbuf.st_mode&S_IFMT) == S_IFDIR)
- return(FIODIR);
-
- if(m[0] == 'r') /* read access? */
- return((S_IREAD&sbuf.st_mode) ? FIOSUC : FIONRD);
- else if(m[0] == 'w') /* write access? */
- return((S_IWRITE&sbuf.st_mode) ? FIOSUC : FIONWT);
- else if(m[0] == 'x') /* execute access? */
- return((S_IEXEC&sbuf.st_mode) ? FIOSUC : FIONEX);
- return(FIOERR); /* what? */
- }
-
-
- /*
- * isdir - returns true if fn is a readable directory, false otherwise
- * silent on errors (we'll let someone else notice the problem;)).
- */
- isdir(fn, l)
- char *fn;
- long *l;
- {
- struct stat sbuf;
-
- if(l)
- *l = 0;
-
- if(stat(fn, &sbuf) < 0)
- return(0);
-
- if(l)
- *l = sbuf.st_size;
- return((sbuf.st_mode&S_IFMT) == S_IFDIR);
- }
-
-
- #if defined(bsd) || defined(nxt) || defined(dyn)
- /*
- * getcwd - NeXT uses getwd()
- */
- char *
- getcwd(pth, len)
- char *pth;
- int len;
- {
- extern char *getwd();
-
- return(getwd(pth));
- }
- #endif
-
-
- /*
- * 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){
- strcpy(s, "~");
- fixpath(s, NLINE); /* let fixpath do the work! */
- hlen = strlen(s);
- if((home=(char *)malloc((strlen(s) + 1) * sizeof(char))) == NULL){
- emlwrite("Problem allocating space for home dir", NULL);
- return(0);
- }
- strcpy(home, s);
- }
-
- 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;
- {
- extern char *sys_errlist[];
- extern int sys_nerr;
-
- return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
- }
-
-
-
- /*
- * getfnames - return all file names in the given directory in a single
- * malloc'd string. n contains the number of names
- */
- char *
- getfnames(dn, n)
- char *dn;
- int *n;
- {
- long l;
- char *names, *np, *p;
- struct stat sbuf;
- #if defined(ct)
- FILE *dirp;
- char fn[DIRSIZ+1];
- #else
- DIR *dirp; /* opened directory */
- #endif
- #if defined(POSIX) || defined(aix) || defined(COHERENT) || defined(isc) || defined(sv3)
- struct dirent *dp;
- #else
- struct direct *dp;
- #endif
-
- *n = 0;
-
- if(stat(dn, &sbuf) < 0){
- switch(errno){
- case ENOENT : /* File not found */
- emlwrite("\007File not found: \"%s\"", dn);
- break;
- #ifdef ENAMETOOLONG
- case ENAMETOOLONG : /* Name is too long */
- emlwrite("\007File name too long: \"%s\"", dn);
- break;
- #endif
- default: /* Some other error */
- emlwrite("\007Error getting file info: \"%s\"", dn);
- break;
- }
- return(NULL);
- }
- else{
- l = sbuf.st_size;
- if((sbuf.st_mode&S_IFMT) != S_IFDIR){
- emlwrite("\007Not a directory: \"%s\"", dn);
- return(NULL);
- }
- }
-
- if((names=(char *)malloc(sizeof(char)*l)) == NULL){
- emlwrite("\007Can't malloc space for file names", NULL);
- return(NULL);
- }
-
- errno = 0;
- if((dirp=opendir(dn)) == NULL){
- sprintf(s,"\007Can't open \"%s\": %s", dn, errstr(errno));
- emlwrite(s, NULL);
- 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){
- (*n)++;
- p = dp->d_name;
- while((*np++ = *p++) != '\0')
- ;
- }
- #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 FIONEX:
- emlwrite("\007Execute permission denied: %s", f);
- break;
- default:
- emlwrite("\007File I/O error: %s", f);
- }
- }
-
-
-
- /*
- * pfnexpand - pico's function to expand the given file name if there is
- * a leading '~'
- */
- char *pfnexpand(fn, len)
- char *fn;
- int len;
- {
- struct passwd *pw;
- register char *x, *y, *z;
- char name[20];
-
- if(*fn == '~') {
- for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
- *y = '\0';
- if(x == fn + 1)
- pw = getpwuid(getuid());
- else
- pw = getpwnam(name);
- if(pw == NULL)
- return(NULL);
- if(strlen(pw->pw_dir) + strlen(fn) > len) {
- return(NULL);
- }
- /* make room for expanded path */
- for(z=x+strlen(x),y=fn+strlen(x)+strlen(pw->pw_dir);
- z >= x;
- *y-- = *z--);
- /* and insert the expanded address */
- for(x=fn,y=pw->pw_dir; *y != '\0'; *x++ = *y++);
- }
- return(fn);
- }
-
-
-
- /*
- * fixpath - make the given pathname into an absolute path
- */
- fixpath(name, len)
- char *name;
- int len;
- {
- register char *shft;
-
- if(*name != '/' && *name != '.'){ /* filenames relative to ~ */
- if(Pmaster && (*name != '~' && strlen(name)+2 <= len)){
-
- for(shft = strchr(name, '\0'); shft >= name; shft--)
- shft[2] = *shft;
-
- name[0] = '~';
- name[1] = '/';
- }
-
- pfnexpand(name, len);
- }
- }
-
-
- /*
- * 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];
-
- #define PUSHD(X) (stack[depth++] = X)
- #define POPD() ((depth > 0) ? stack[--depth] : "")
-
- if(*path == '~'){
- fixpath(path, len);
- strcpy(s, path);
- }
- else if(*path != C_FILESEP)
- sprintf(s, "%s%c%s", base, C_FILESEP, path);
- else
- strcpy(s, path);
-
- p = s;
- for(i=0; s[i] != '\0'; i++){ /* pass thru path name */
- if(s[i] == '/'){
- if(p != s)
- PUSHD(p); /* push dir entry */
- p = &s[i+1]; /* advance p */
- s[i] = '\0'; /* cap old p off */
- continue;
- }
-
- if(s[i] == '.'){ /* special cases! */
- if(s[i+1] == '.' /* parent */
- && (s[i+2] == '/' || s[i+2] == '\0')){
- if(!strcmp(POPD(),"")) /* bad news! */
- return(0);
-
- i += 2;
- p = (s[i] == '\0') ? "" : &s[i+1];
- }
- else if(s[i+1] == '/' || s[i+1] == '\0'){ /* no op */
- i++;
- p = (s[i] == '\0') ? "" : &s[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 */
- }
-
-
- /*
- * tmpname - return a temporary file name in the given buffer
- */
- void
- tmpname(name)
- char *name;
- {
- sprintf(name, "/tmp/pico.%d", getpid()); /* tmp file name */
- }
-
-
- /*
- * Take a file name, and from it
- * fabricate a buffer name. This routine knows
- * about the syntax of file names on the target system.
- * I suppose that this information could be put in
- * a better place than a line of code.
- */
- 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)
- char *fn;
- {
- extern FILE *ffp;
-
- if ((ffp=fopen(fn, "w")) == NULL) {
- emlwrite("Cannot open file for writing", NULL);
- return (FIOERR);
- }
-
- return (FIOSUC);
- }
-
-
- /*
- * Close a file. Should look at the status in all systems.
- */
- ffclose()
- {
- extern FILE *ffp;
-
- if (fclose(ffp) != FALSE) {
- emlwrite("Error closing file", NULL);
- return(FIOERR);
- }
-
- return(FIOSUC);
- }
-
-
- /*
- * P_open - run the given command in a sub-shell returning a file pointer
- * from which to read the output
- *
- * note:
- * For OS's other than unix, you will have to rewrite this function.
- * Hopefully it'll be easy to exec the command into a temporary file,
- * and return a file pointer to that opened file or something.
- */
- FILE *P_open(s)
- char *s;
- {
- return(popen(s, "r"));
- }
-
-
-
- /*
- * P_close - close the given descriptor
- *
- */
- P_close(fp)
- FILE *fp;
- {
- return(pclose(fp));
- }
-
-
-
- /*
- * worthit - generic sort of test to roughly gage usefulness of using
- * optimized scrolling.
- *
- * note:
- * returns the line on the screen, l, that the dot is currently on
- */
- worthit(l)
- int *l;
- {
- int i; /* l is current line */
- unsigned below; /* below is avg # of ch/line under . */
-
- *l = doton(&i, &below);
- below = (i > 0) ? below/(unsigned)i : 0;
-
- return(below > 3);
- }
-
-
-
- /*
- * pico_new_mail - just checks mtime and atime of mail file and notifies user
- * if it's possible that they have new mail.
- */
- pico_new_mail()
- {
- int ret = 0;
- static time_t lastchk = 0;
- struct stat sbuf;
- char inbox[256], *p;
-
- if(p = (char *)getenv("MAIL"))
- sprintf(inbox, p);
- else
- sprintf(inbox,"%s/%s", MAILDIR, getlogin());
-
- if(stat(inbox, &sbuf) == 0){
- ret = sbuf.st_atime <= sbuf.st_mtime &&
- (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
- lastchk = sbuf.st_mtime;
- return(ret);
- }
- else
- return(ret);
- }
-
-
-
- /*
- * time_to_check - checks the current time against the last time called
- * and returns true if the elapsed time is > timeout
- */
- time_to_check()
- {
- static time_t lasttime = 0L;
-
- if(!timeout)
- return(FALSE);
-
- if(time((long *) 0) - lasttime > (time_t)timeout){
- lasttime = time((long *) 0);
- return(TRUE);
- }
- else
- return(FALSE);
- }
-
-
- /*
- * sstrcasecmp - compare two pointers to strings case independently
- */
- sstrcasecmp(s1, s2)
- QcompType *s1, *s2;
- {
- register char *a, *b;
-
- a = *(char **)s1;
- b = *(char **)s2;
- while(toupper(*a) == toupper(*b++))
- if(*a++ == '\0')
- return(0);
-
- return(toupper(*a) - toupper(*--b));
- }
-
-
- #ifdef TIOCGWINSZ
- /*
- * winch_handler - handle window change signal
- */
- SIGTYPE winch_handler()
- {
- struct winsize win;
- extern int resize_pico();
-
- signal(SIGWINCH, winch_handler);
-
- if (ioctl(0, TIOCGWINSZ, &win) == 0) {
- if (win.ws_col && win.ws_row)
- resize_pico(win.ws_row - 1, win.ws_col);
- }
- }
- #endif /* TIOCGWINSZ */
-
-
- #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
-