/*
 * this file is a part of yabbs - yet another bulletin board system.
 * Copyright (C) 1993, 1994 Alex Wetmore.  
 * email: alex@phred.org
 * address: 6 rech ave
 *          oreland pa 19075
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * common.c   - part of the yabbs bulletin board package by alex wetmore
 *              see bbs.c for information on the system and copying.
 */

#define UTIL_COMMON_C

#include <stdio.h>
#include <fcntl.h>
#if TERMIO
#include <termio.h>                         /* termio terminal control  */
#else
#include <sgtty.h>                          /* sgtty terminal control   */
#endif
#include <sys/ioctl.h>                      /* i/o control              */
#include "bbs_struct.h"
#include "bbs_files.h"
#include "util_common.h"

int line, usernum;
urec user[MAXUSERS];
#if TERMIO
struct termio term, save;
#else
struct sgttyb term, save;
#endif

/*
 * return T or F depending on whether the user has <option> enabled
 */

char opton(int option, int u) {
    if ((user[u].options & option) == option) 
        return 'T'; 
    else 
        return 'F';
}


char lower(char i) {
    if ((i >= 'A') && (i <= 'Z')) i = i + ('a' - 'A');
    return i;
}

char upper(char i) {
    if ((i >= 'a') && (i <= 'z')) i = i + ('A' - 'a');
    return i;
}

/*
 * take a 32bit integer and turn it into a string that has a + wherever
 * a bit is high, and a minus wherever it is low.  Used to graphically
 * show access control.
 */

void inttosubs(char *subs, unsigned long int *subsi) {
    char        i;

    for (i = 0; i < MBASES; i++)
        if (*subsi & power(2, i)) subs[i] = '+'; else subs[i] = '-';
    subs[MBASES] = 0;
}
/*
 * return true if a <fname> exists, otherwise return false
 */

int fexist(char *fname) {
    FILE *fi;

    if ((fi = fopen(fname, "r")) == NULL) {
        return FALSE;
    } else {
        fclose(fi);
        return TRUE;
    }
}

/*
 * read a character from the user.  Do some translating because it is easy.
 */

#ifndef STDIN
#define STDIN 0
#endif

char readc(void) {
    int ch;
    
    line = 1;
    while (1) {
        ch = fgetc(stdin);
        if (((ch >= 32) && (ch <= 127)) || 
             (ch == 13) || (ch == 10) || (ch == '\t') || (ch == 8))
                return ch;
    }
}

/*
 * write a character to the user
 */

void writec(char c) {
    if (((c >= 32) && (c < 127)) || 
        (c == 27) ||
        (c == 10) || 
        (c == 13) ||
        (c == 8) ||
        (c == 7)) fputc(c, stdout);
}

/*
 * wait for the user to hit a character that is in <okaych>.  When we get one
 * return.
 */

char waitfor(char *okaych) {
    char ch;
    int  x, done = FALSE;

    done = FALSE;
    while (!done) {
        ch = lower(readc());
        for (x = 0; x < strlen(okaych); x++)
            if (ch == lower(okaych[x])) done = TRUE;
    }
    return ch; 
}

/*
 * put a line on the screen.  Everytime we see a '\n' check to see if we have
 * reached the end of the users screen.  If so prompt them with [more] and
 * reset the line variable, otherwise just keep printing.  '\n' is printed
 * as '\r\n' to work with more terminals.
 */

void sputs(char *st) {
    int     i, l = strlen(st);
    static  nowrite;

    for (i = 0; i < l; i++) {
        if ((st[i] == '\n') && (!nowrite)) {
            writec('\r');
            if ((++line == user[0].scrlen) && (user[0].scrlen > 0)) {
                printf("\n<slam that key>"); fflush(stdout);
                readc();
                printf("\r                    \r");
            } else { 
                writec(st[i]); 
            }
        } else if (!nowrite) {
            writec(st[i]); 
        }
    }
    fflush(stdout);
}

/*
 * usage:  readl(string, maxlen, echo)
 * desc:   read a string
 * input:  string - string to save to
 *         maxlen - maximum length of string
 *         echo - if TRUE then echo, otherwise no echoing (for pws).
 * output: error stuff
 */

int readl(char *ptr, register int maxlen, register int echo) {
    int i, x, ch, done;

    i = -1;
    *ptr = 0;
    done = 0;
    while (!done) {
        ch = readc();
        if ((ch == 10) || (ch == 13)) done = 1;
        else if (((ch == 127) || (ch == 8)) && (i >= 0)) {
            *(ptr + i--) = 0;
            if (echo) sputs("\b \b");
        } else if ((ch == 21) && (i >= 0)) {
            while (i >= 0) {
                *(ptr + i--) = 0;
                if (echo) { sputs("\b \b"); }
            }
        } else if ((ch == 18) || (ch == 12)) {
            sputs("\n");
            for (x = 0; x <= i; x++) {
                if (echo) { writec(*(ptr + x)); fflush(stdout); }
            }
        } else if ((ch == '\t')) {
            for (x = 0; (x < 4) && (i < maxlen - 1); x++) {
                *(ptr + ++i) = ' ';
                if (echo) { writec(' '); fflush(stdout); }
            }
            *(ptr + i + 1) = 0;
        } else if ((i < maxlen - 1) && (ch >= 32) && (ch <= 126)) {
            *(ptr + ++i) = ch;
            *(ptr + i + 1) = 0;
            if (echo) { writec(ch); fflush(stdout); }
        }
    }
    sputs("\n");
    return ++i;
}

/*
 * read a line in from the user.
 */

void sgets(char *st, int max) {
    readl(st, max, TRUE); 
}

/*
 * read a password from the user.  8 chars max, no echo.
 */

void sgetpw(char *st) {
    readl(st, 8, FALSE);
}

/*
 * dump a file to the user.
 */

void dump(char *filename) {
    FILE    *fi;
    char    s[255];
    int x;

    fi = fopen(filename, "r");
    if (fi == NULL) {
        sprintf(s, "error opening [%s] for input.\nPlease tell the sysop\n", filename);
        sputs(s);
    }
    x = 0;
    while (!feof(fi)) {
        s[x++] = fgetc(fi); s[x] = 0;
        if (x == 80) { sputs(s); x = 0; }
    }
    sputs(s);
    fclose(fi);
}

/*            n
 * return base .  Integers only.
 */

unsigned long power(int base, int n) {
    int                 i;
    unsigned long int   p;

    p = 1;
    for (i = 1; i <= n; i++)
        p = p * base;
    return p;
}

/*
 * prompt the user with <st> and wait for a yes/no response.  Return
 * true if yes, false if no.  Default to yes if <def> is true, else
 * default to no.
 */

int ynprompt(char *st, int def) {
    char    yn;

    sputs(st);
    yn = waitfor("yn\n\r ");
    if      ((def)  && (yn == 'n')) { sputs("no\n"); return FALSE; }
    else if ((def)  && (yn != 'n')) { sputs("yes\n"); return TRUE; }
    else if ((!def) && (yn == 'y')) { sputs("yes\n"); return TRUE; }
    else if ((!def) && (yn != 'y')) { sputs("no\n"); return FALSE; }
}

/*
 * copy <n> characters in <src> to <dup> starting at <beg>
 */

void strmid(char *dup, char *src, int beg, int n) {
    int         i;

    for (i = beg; i < (beg + n); i++) {
        dup[i - beg] = src[i];
    }
    dup[n] = 0;
}

/*
 * write out the base defination file.  I don't think that it is ever used
 * here, just in baseed.
 */

void writebases(brec *base) {
    FILE    *fo;
    char    s[255];

    fo = fopen(F_BASES, "wb");
    if (fo == NULL) {
        sprintf(s, "error opening [%s] for wb.", F_BASES);
        sputs(s);
        killconnection(K_FERR);
    }
    fwrite(base, sizeof(brec), MBASES, fo);
    fclose(fo);
}

/*
 * read in a base defination file.  If there isn't one, crash the program
 * and tell the sysop to make one.  Don't let this happen to your users, it
 * will just leave them hanging if they are networked.
 */

void readbases(brec **basep) {
    FILE    *fi;
    int     i;
    brec    *base;

    base = *basep;

    if ((fi = fopen(F_BASES, "rb")) == NULL) {
        fprintf(stderr, "creating new bases.dat\n");
        for (i = 0; i < MBASES; i++) {
            strcpy(base[i].title, S_NULLBASE);
            strcpy(base[i].dfile, "null.dat");
            base[i].max = 50;
        }
        writebases(base);
        fi = fopen(F_BASES, "rb");
    }
    fread(base, sizeof(brec), MBASES, fi);
    fclose(fi);
}

/*
 * read the user defination file and stick it into memory.
 */

void readusers() {
    FILE    *fi;
    int     r, i;

    if ((fi = fopen(F_USERS, "r")) == NULL) {
        sputs("\nno user file, making a new user\n");
        sputs("user name: "); sgets(user[0].name, 8);
        sputs("password: "); sgets(user[0].pass, 8);
        user[0].sub = D_SUBS;
        user[0].access = D_SUBS;
        user[0].scrlen = D_SCRLEN;
        user[0].options = D_OPT;
        for (i = 0; i < MBASES; i++) user[0].lastmsg[i] = 0;
        usernum = 1;
        writeusers();
        fi = fopen(F_USERS, "r");
    }
    usernum = 0;
    while (!feof(fi)) {
        r = fread(&(user[usernum]), sizeof(urec), 1, fi);
        if (r == 1) usernum++;
    }
    sputs("\n");
    fclose(fi);
}

void waitkey(void) {
    sputs("\n<slam that key>"); readc();
    sputs("\r               \r");
}

/*
 * write the user defination file from the user structure in memory.
 */

void writeusers(void) {
    int fo;

    fo = open(F_USERS, O_WRONLY | O_TRUNC | O_CREAT, UMASK);
    if (fo == -1) {
        sputs("error opening users.dat for write");
        killconnection(K_FERR);
    }
    write(fo, user, sizeof(urec) * usernum);
    close(fo);
}

/*
 * Put all of the vital information for user <u> on the screen.
 */

void drawuser(int u) {
    char    temp[255];
    int     i;

    sprintf(temp, "\nnum: [%d]  name: [%s]  scrlen: [%u]",
        u, user[u].name, user[u].scrlen);
    sputs(temp);
    sprintf(temp, "  expert: [%c]  sysop: [%c]\n", opton(O_XPERT, u), 
        opton(O_SYSOP, u));
    sputs(temp);

    sputs("        ");
    for (i = 0; i < MBASES; i++) {
        temp[i] = i + BASEOF;
    }
    temp[MBASES] = 0;
    sputs(temp); sputs("\n");
    inttosubs(temp, &user[u].sub);
    sputs("bases:  "); sputs(temp); sputs("\n");
    inttosubs(temp, &user[u].access);
    sputs("access: "); sputs(temp); sputs("\n\n");
}



void noecho() {
#if TERMIO
    /* Get terminal modes.                                              */
    ioctl(2, TCGETA, &term);

    /* Save modes and set certain variables dependent on modes.         */
    save = term;

    /* Set the modes to the way we want them.                           */
    term.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
    term.c_oflag |=  (OPOST|ONLCR|TAB3);
    term.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;

    /* set the new modes                                                */
    ioctl(2, TCSETAW, &term);
#else                                   
    /* Get terminal modes.                                              */
    ioctl(2, TIOCGETP, &term);

    /* Save modes and set certain variables dependent on modes.         */
    save = term;

    /* Set the modes to the way we want them.                           */
    term.sg_flags |= CBREAK;
    term.sg_flags &= ~(ECHO|XTABS);
            
    /* set the new modes                                                */
    ioctl(2, TIOCSETN, &term);
#endif
}

void echo(void) {
#if TERMIO
    ioctl(2, TCSETAW, &save);
#else
    ioctl(2, TIOCSETN, &save);
#endif
}
