/*
 * 4m v2.15 (unix.c)
 * (C) Copyright 1990 by Carrick Sean Casey
 * (C) Copyright 1993 by Scott Chasin and Michaeljon Miller
 *
 * 3-22-93 See file COPYING for copyright information.
 *
 * Unix system library
 */

#include "4m.h"
#include "externs.h"
#include <pwd.h>

#ifdef SOLARIS
# include <sys/dirent.h>
#else
# include <sys/dir.h>
#endif


#if !defined(NO_STDLIB_H)
#include <stdlib.h>
#endif

#include <stdio.h>

#ifdef stty
# undef stty
#endif

#ifdef gtty
# undef gtty
#endif

#ifndef SYSV
# include <sgtty.h>
# define TTYSTRUCT sgttyb
# ifndef DGUX
#  define stty(fd,buf) ioctl((fd),TIOCSETN,(buf))
#  define gtty(fd,buf) ioctl((fd),TIOCGETP,(buf))
# endif
#else
# include <termio.h>
# define TTYSTRUCT termio
# define stty(fd,buf) ioctl((fd),TCSETA,(buf))
# define gtty(fd,buf) ioctl((fd),TCGETA,(buf))
#endif

#ifdef RESIZE_ME
# include <signal.h>
# ifndef SIGWINCH
#  undef RESIZE_ME
# endif /* SIGWINCH */
#endif  /* RESIZE_ME */

void (*t_scroll) ();

#ifdef SYSV
  struct termio tty;
  struct termio oldtty;
#else
  struct sgttyb tty;
  struct sgttyb oldtty;
  struct tchars chars;
#endif

int TC_li = 0;
int TC_co = 0;

char *TC_cl = NULL, *TC_so = NULL, *TC_se = NULL, *TC_cm = NULL, *TC_ce = NULL, *TC_cs = NULL, *TC_sf = NULL, *TC_nl = NULL, *TC_al = NULL, *TC_dl = NULL, *TC_SF = NULL, *TC_AL = NULL, *TC_DL = NULL, *TC_sr = NULL, *TC_is = NULL, *TC_te = NULL, *TC_vi = NULL, *TC_ve = NULL, *TC_rc = NULL, *TC_sc = NULL;

#define IN_TERM
# include "../h/term.h"
#undef IN_TERM

static char termbuf[1024];
static char termstr[1024];

int speed;

static int speeds[] =
{0, 50, 75, 110, 134, 150, 200, 300, 600,
1200, 1800, 2400, 4800, 9600, 19200, 38400};

extern char *ontime();

#ifdef RESIZE_ME

extern int ioctl ();
extern int clear_screen ();
extern char *getenv ();
extern uid_t getuid ();
extern void perror ();
extern char *strchr ();
extern char *strcat ();
extern int fflush ();
extern int _flsbuf ();
extern int printf ();
extern int write ();
extern time_t time ();
extern void build ();
extern int strcasecmp();
void putpad();

SIGTYPE 
win_resize(sig)
     int sig;
{
    struct winsize winsize;

    ioctl(0, TIOCGWINSZ, &winsize);

    TC_co = winsize.ws_col;
    lines = winsize.ws_row;

    clear_screen(0);
}

#endif  /* RESIZE_ME */

/*
 * g e t l o g i n i d
 *
 * Returns a pointer to the user's login id. Returns NULL if not available.
 *
 */

char *
getloginid()
{
    char *loginid = NULL;
    struct passwd *pw;

    if (((loginid = (char *) getlogin()) == NULL) 
          || *loginid == '\0')
        if ((pw = getpwuid(getuid())) != NULL)
            loginid = pw->pw_name;

    return (loginid);
}

char *getgecos()
{
    struct passwd *pw;
    char *tmp;

    if ((pw = getpwuid(getuid())) != 0) {
        if ((tmp = (char *) index(pw->pw_gecos, ',')) != 0)
            *tmp = '\0';
        return (pw->pw_gecos);
    }
    tmp = NULL;
    return (tmp);
}

/*
 * l i n e b u f f e r
 *
 * Set line buffering for an open file pointer. Output will be flushed every
 * newline.
 *
 */

void linebuffer(fp)
     FILE *fp;
{
#if !defined(SYSV) && !defined(DYNIX)
    extern int setlinebuf();
    setlinebuf(fp);
#else
#if !defined(pyr)
    (void) setvbuf(fp, NULL, _IOLBF, 0);
#endif
#endif
}

/*
 * p u s h b a c k
 *
 * Push a character into the terminal's input buffer.
 *
 */

void pushback(c)
     char c;
{
#if !defined(SYSV) && !defined(DYNIX)
    if (ioctl(0, TIOCSTI, &c) < 0)
        perror("TIOCSTI ioctl failed");
#else
    perror("ungetc() failed");
#endif
}

/*
 * g e t w i n s i z e
 *
 * Get the number of lines and columns for this tty.
 *
 */

void getwinsize()
{
    char env_lines[80], env_cols[80];
    char *xx;
    int xx_lines = 24, xx_cols = 80;
#ifdef TIOCWINSZ
    struct winsize win;
#endif

    if ((xx = getenv("LINES")) != NULL) {
        strcpy(env_lines, xx);
        xx_lines = atoi(env_lines);
    }
    if ((xx = getenv("COLUMNS")) != NULL) {
        strcpy(env_cols, xx);
        xx_cols = atoi(env_cols);
    }
#ifdef TIOCWINSZ
 /* for unixes that winsize or TIOCGWINSZ */
    if (ioctl(0, TIOCGWINSZ, &win) < 0) {
        ttyinfo.rows = xx_lines;
        ttyinfo.cols = xx_cols;
    } else {
        ttyinfo.rows = win.ws_row;
        ttyinfo.cols = win.ws_col;
    }
#else
    ttyinfo.rows = xx_lines;
    ttyinfo.cols = xx_cols;
#endif

    return;
}

/*
 * t i l d e x p a n d
 *
 * Translate paths prefixed with the tilde (~) in proper system paths.
 *
 */

char *
tildexpand(s)
     char *s;
{
    struct passwd *pw;
    char login[17];
    char *p = login;
    static char path[256];

 /* Has to start with a tilde */

    if (*s++ != '~')
        return (s);

 /* Skim off the login id */

    while (((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') ||
            strchr(".-_", *s)) && ((p - login) < 17))
        *p++ = *s++;
    *p = '\0';

 /* Get home directory from password file */

    if ((pw = getpwnam(login)) == NULL)
        return (NULL);

    strcpy(path, pw->pw_dir);
    strcat(path, s);

    return (path);
}

/*
 * s e t _ t e r m  (SYSV)
 *
 * System V terminal settings require placing the tty in non canonical mode
 * (don't echo when typed) and ECHO off (don't echo at all). Also ECHONL for
 * good measure.
 *
 * The ugly part is we have to use timeouts for each character.
 *
 * s e t _ t e r m  (BSD)
 *
 * Berkeley Unix terminal settings require putting the terminal in CBREAK mode
 * (not quite raw) and turning ECHO off. These symbols are getting odd
 * (tiocgetp)..
 *
 */

void set_term()
{
    gtty(fileno(stdin), &tty);
    gtty(fileno(stdin), &oldtty);

#ifdef SYSV
    tty.c_lflag &= ~(ICANON | ECHO | ECHONL);
#else
    tty.sg_flags |= CBREAK;
    tty.sg_flags &= ~ECHO;
#endif

#ifdef SYSV
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 0;
    tty.c_cc[VINTR] = 3;
#else
    ioctl(fileno(stdin), TIOCGETC, &chars);
    chars.t_intrc = 3;
    ioctl(fileno(stdin), TIOCSETC, &chars);
#endif

    stty(fileno(stdin), &tty);

#ifdef SYSV
    speed = speeds[tty.c_cflag & 017];
#else
    speed = speeds[(int)tty.sg_ispeed];
#endif

#ifdef RESIZE_ME
    signal(SIGWINCH, win_resize);
#endif  /* RESIZE_ME */

    return;
}

/*
 * u n s e t _ t e r m
 *
 * a brave hack where no man has gone before
 *
 *
 */

void unset_term()
{

/*  showcursor();  */

    if (TC_cs)
        tputs(TC_cs, lines - 1, put);

    if (TC_is)
        tputs(TC_is, lines, put);

    if (TC_te)
        tputs(TC_te, lines, put); 

    stty(fileno(stdin), &oldtty);

#ifndef SYSV
    ioctl(fileno(stdin), TIOCSETC, &chars);
#endif

    gotoxy (0, lines - 1);
    fflush(stdout);

    return;
}

/*
 * p u t
 *
 * Single character output. Echo to file if necessary. Unfortunately this can no
 * longer be a macro expansion, but it is not too heavily used.
 *
 */

int 
put(c)
     char c;
{
    putchar(c);
    fflush(stdout);

    return (0);
}

/*
 * t e r m c a p
 *
 * Set terminal capabilities. Termcap(3) will handle reasonable terminal cursor
 * addressing requests, of which we use the clear screen function, and direct
 * cursor addressing.
 *
 * /etc/termcap or the environment variable "TERMCAP" contains possible terminal
 * types. Many of these entries contain workarounds for bugs in vi, and might
 * not reflect best codes needed.
 *
 */

int 
termcap(term)
     char *term;
{
    char *termptr = termstr;
    char *linenv, *cols;
    extern char *getenv();

    extern void tcapscroll_reg();
    extern void tcapscroll_delins();

    if (!strcasecmp(term, "none"))
        term = "unknown";

    if (tgetent(termbuf, term) <= 0) {
        printf("4m: Terminal %s not supported.\n", term);
        exit(1);
    } else {
        if ((linenv = getenv("LINES")) == NULL) {
            lines = tgetnum("li");
            TC_li = tgetnum("li");
        } else {
            lines = atoi(linenv);
            TC_li = atoi(linenv);
        }
        if ((cols = getenv("COLUMNS")) == NULL)
            TC_co = tgetnum("co");
        else
            TC_co = atoi(cols);

        TC_cl = tgetstr("cl", &termptr);
        TC_ce = tgetstr("ce", &termptr);
        TC_cm = tgetstr("cm", &termptr);
        TC_se = tgetstr("se", &termptr);
        TC_so = tgetstr("so", &termptr);
        TC_cs = tgetstr("cs", &termptr);
        TC_sf = tgetstr("sf", &termptr);
        TC_SF = tgetstr("SF", &termptr);
        TC_nl = tgetstr("nl", &termptr);
        TC_al = tgetstr("al", &termptr);
        TC_dl = tgetstr("dl", &termptr);
        TC_AL = tgetstr("AL", &termptr);
        TC_DL = tgetstr("DL", &termptr);
        TC_sr = tgetstr("sr", &termptr);
        TC_ve = tgetstr("ve", &termptr);
        TC_vi = tgetstr("vi", &termptr);
        TC_rc = tgetstr("rc", &termptr);
        TC_sc = tgetstr("sc", &termptr);
        TC_is = tgetstr("is", &termptr);
        TC_te = tgetstr("te", &termptr);

        if (!TC_cm || !TC_cl) {
            printf("4m: This terminal can not run 4m in full screen mode.\n");
            printf("4m: You may run 4m in \"dumb\" mode with the -d option.\n");
            unset_term();
            exit(1);
        }
        if (!TC_cs || !TC_sr) { /* some xterm's termcap entry is missing
                                 * entries */
            if (!strcmp(term, "xterm") || !strcmp(term, "ansi")) {
                if (!TC_cs)
                    TC_cs = "\033[%i%d;%dr";
                if (!TC_sr)
                    TC_sr = "\033[M";
            }
        }
        if (!TC_dl || !TC_sr) {
            if (!strcmp(term, "vt100")) {
                if (!TC_dl)
                    TC_dl = "\\E[M";
                if (!TC_sr)
                    TC_sr = "\033[M";
            }
        }
        if (TC_cs && TC_sr) {
            if (TC_sf == NULL)  /* assume '\n' scrolls forward */
                TC_sf = "\n";
            t_scroll = tcapscroll_reg;;
        } else if ((TC_DL && TC_AL) || (TC_dl && TC_al)) {
            t_scroll = tcapscroll_delins;
        } else {
            t_scroll = NULL;
        }

        if (TC_nl == NULL)
            TC_nl = "\n";

        return TRUE;
    }
}

void title()
{
    char buffer[80];
    register int id;

    if (dumb)
        return;

    hidecursor();
    gotoxy(0, 0);
    clear_eol();
    setinverse();
    for (id = 0; id < TC_co; id++)
        mbuf[id] = ' ';
    mbuf[TC_co] = 0;
    sprintf(buffer, "*** 4m Intercom V%s ***", VERSION);
    memcpy((char *) &mbuf[(TC_co - strlen(buffer)) / 2],
           buffer, strlen(buffer));
    write(1, mbuf, strlen(mbuf));
    setnormal();

    for (id = 0; id < TC_co; id++)
        mbuf[id] = ' ';
    mbuf[TC_co] = 0;
    gotoxy(0, lines - 3);
    setinverse();
    write(1, mbuf, TC_co);
    setnormal();
    gotoxy(0, lines - 3);
    showcursor();

    return;
}

/*
 *  s t a t u s
 *
 *  If flag is TRUE, update status bar.  Otherwise,
 *  only update if a minute has passed since last update.
 *
 */

void status(flag)
     int flag;
{
    static char thistime[10], howlong[10];
    extern char *local_ampm();
    char *lam, *t_on;

    if (dumb)
        return;

    lam = local_ampm(time(NULL), 0);
    t_on = ontime(startup_t);

    if (strcmp(thistime, lam) || strcmp(howlong, t_on) || flag) {
        strcpy(thistime, lam);
        strcpy(howlong, t_on);
        gotoxy(0, lines - 3);
        build(gv.statusbar, NIL);
        gotoxy(ipos, lines - 2);
    }

    return;
}

void
putpad(str)
     char *str;
{
    tputs(str, 1, put);

    return;
}

void
tcapscrollregion(top, bot)
     int top;
     int bot;
{
    putpad(tgoto(TC_cs, bot, top));

    return;
}

void 
tcapscroll_reg(from, to, n)
     int from;
     int to;
     int n;
{
    int i;

    if (to == from)
        return;
    tcapscrollregion(to, from + n - 1);
    gotoxy(from + n - 1, 0);
    for (i = from - to; i > 0; i--)
        putpad(TC_sf);
    tcapscrollregion(1, lines - 4); /* mjm */

    return;
}

void 
tcapscroll_delins(from, to, n)
     int from;
     int to;
     int n;
{
    int i;

    if (to == from)
        return;

    if (TC_DL && TC_AL) {
        if (to < from) {
            gotoxy(to, 0);
            putpad(tgoto(TC_DL, 0, from - to));
            gotoxy(to + n, 0);
            putpad(tgoto(TC_AL, 0, from - to));
        } else {
            gotoxy(from + n, 0);
            putpad(tgoto(TC_DL, 0, to - from));
            gotoxy(from, 0);
            putpad(tgoto(TC_AL, 0, to - from));
        }
    } else {  /* must be dl and al */
        if (abs(from - to) > 1) {
            tcapscroll_delins(from, (from < to) ? to - 1 : to + 1, n);
            if (from < to)
                from = to - 1;
            else
                from = to + 1;
        }
        if (to < from) {
            gotoxy(to, 0);
            for (i = from - to; i > 0; i--)
                putpad(TC_dl);
            gotoxy(to + n, 0);
            for (i = from - to; i > 0; i--)
                putpad(TC_al);
        } else {
            gotoxy(from + n, 0);
            for (i = to - from; i > 0; i--)
                putpad(TC_dl);
            gotoxy(from, 0);
            for (i = to - from; i > 0; i--)
                putpad(TC_al);
        }
    }

    return;
}

