/*
  Pirate Bulletin Board System
  Copyright (C) 1990, Edward Luke
  
  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 1, 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.
  
  
  Email address:
  lush@Athena.EE.MsState.EDU
  */
#include "bbs.h"
#include "io.h"

#include <sys/file.h>

int mot ;
static int quiting ;


struct user currentuser ;
int usernum ;
char currboard[STRLEN] ;
int selboard = 0 ;

extern char *boardmargin() ;
extern char *filemargin() ;

char genbuf[4096] ;

exit_warning()
{
    bell() ;
    return 0 ;
}

Maintenance()
{
    docmd("Maintenance Menu","Enter maintenance command: ",'h',maintlist,boardmargin) ;
    clear() ;
    return 0 ;
}


Xyz()
{
    report("XYZ menu") ;
    docmd("MISC utilities Menu.","Enter utility command: ",'h',xyzlist,boardmargin) ;
    clear() ;
    return 0 ;
}

Talk()
{
    report("Enter Talk menu") ;
    docmd("Interactive Talk Menu","Enter command: ",'h',talklist,boardmargin) ;
    report("Exit Talk menu") ;
    clear() ;
    return 0 ;
}


Files()
{
    extern struct user_info uinfo ;
    report("Files menu") ;
    uinfo.mode = ULDL ;
    substitute_record(ULIST,(void *)&uinfo, sizeof(uinfo), utmpent) ;
    docmd("FILE Transfer Menu.","Enter File Transfer command: ",'s',filelist,filemargin) ;
    clear() ;
    uinfo.mode = IDLE ;
    substitute_record(ULIST,(void *)&uinfo, sizeof(uinfo), utmpent) ;
    return 0 ;
}

Mail()
{
    int cmd ;
    extern struct user_info uinfo ;
    
    report("Mail menu") ;
    uinfo.mode = MAIL ;
    substitute_record(ULIST,(void *)&uinfo,sizeof(uinfo),utmpent) ;
    if(chkmail())
      cmd = 'n' ;
    else
      cmd = 's' ;
    docmd("MAIL System Menu.","Enter MAIL command: ",cmd,maillist,boardmargin) ;
    uinfo.mode = IDLE ;
    substitute_record(ULIST,(void *)&uinfo,sizeof(uinfo),utmpent) ;
    clear() ;
    return 0 ;
}

printuent(uentp)
struct user *uentp ;
{
    static int i ;
    
    if(uentp == NULL) {
        move(3,0) ;
        prints("%-16s %-24s %-10s\n","User Id","User Name","Termtype") ;
        i = 3 ;
        return 0;
    }
    if(uentp->userid[0] == '\0')
      return 0 ;
    if(i == 22) {
        int ch ;
        standout() ;
        prints("--MORE--") ;
        standend() ;
        while((ch = igetch()) != EOF) {
            if(ch == '\n' || ch == '\r' || ch == ' ')
              break ;
            if(ch == 'q') {
                move(23,0) ;
                clrtoeol() ;
                return QUIT ;
            }
            bell() ;
        }
        move(3,0) ;
        prints("%-16s %-24s %-10s\n","User Id","User Name","Termtype") ;
        i = 3 ;
        clrtobot() ;
    }
    prints("%-16s %-24s %-10s\n",uentp->userid,uentp->username,uentp->termtype);
    i++ ;
    return 0 ;
}

Users()
{
    report("User List") ;
    printuent((struct user *)NULL) ;
    if(apply_record(PASSFILE,printuent,sizeof(struct user)) == -1) {
        prints("No Users Exist") ;
        pressreturn() ;
        return 0;
    }
    clrtobot() ;
    return 0;
}

Create()
{
    struct fileheader newboard ;
    char *s ;
    extern int numboards ;
    
    clear() ;
    bzero(&newboard,sizeof(newboard)) ;
    prints("Create a Board:") ;
  cagain:
    getdata(3,0,"Enter Board Name (use single word): ",newboard.filename,
            STRLEN,DOECHO,NULL) ;
    for(s = newboard.filename;*s!='\0';s++)
      if(!isalpha(*s)) {
          prints("boardname is invalid\n") ;
          goto cagain ;
      }
    getdata(4,0,"Enter Board description: ",newboard.title,STRLEN,DOECHO,NULL) ;
    strcpy(genbuf,"boards/") ;
    strcat(genbuf,newboard.filename) ;
    if(mkdir(genbuf,0755) == -1) {
        prints("\nError: Bad Board Name\n") ;
        pressreturn() ;
        clear() ;
        return -1 ;
    }
    if(append_record(BOARDS,&newboard,sizeof(newboard)) == -1) {
        pressreturn() ;
        clear() ;
        return -1 ;
    }
    numboards = -1 ;
    prints("\nNew Board Added\n") ;
    pressreturn() ;
    clear()  ;
    return 0 ;
}

int at ;

filepr(fhdrp,mode)
struct fileheader *fhdrp ;
int mode ;
{
    if(at == -1)
      return 0;
    if(at == 23) {
        int ch ;
        
        standout() ;
        prints("-- More --") ;
        standend() ;
        while((ch = igetch()) != EOF) {
            if(ch == 'q') {
                move(23,0) ;
                clrtoeol() ;
                return QUIT;
            }
            if(ch == '\n' || ch == '\r' || ch == ' ')
              break ;
            bell() ;
        }
        move(5,0) ;
        clrtobot() ;
        at = 5 ;
    }
    prints("%c %-20s %s\n",(mode&FILE_READ)?'*':' ',
           fhdrp->filename,fhdrp->title) ;
    at++ ;
    return 0 ;
}

Boards()
{
    move(3,0) ;
    clrtobot() ;
    prints("List of Boards\n") ;
    prints("  Name                 Title\n") ;
    at = 5 ;
    
    apply_boards(filepr) ;
    return 0 ;
}

char *
  bfile(boardname)
char *boardname ;
{
    static char buf[STRLEN] ;
    
    strcpy(buf,"boards/") ;
    strcat(buf,boardname) ;
    return buf ;
}

g_board_names(fhdrp,mode)
struct fileheader *fhdrp ;
int mode ;
{
    AddNameList(fhdrp->filename) ;
    return 0 ;
}

make_blist()
{
    CreateNameList() ;
    apply_boards(g_board_names) ;
}

Select()
{
    char bname[STRLEN] ;
    struct stat st ;
    char buf[512] ;
    
    make_blist() ;
    move(0,0) ;
    prints("Select a Board Name (Be sure to include capital letters)\n") ;
    prints("Select Board: ") ;
    clrtoeol() ;
    namecomplete((char *)NULL,bname) ;
    if((*bname == '\0') || (stat(bfile(bname),&st) == -1)) {
        move(2,0) ;
        prints("Invalid Board Name") ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return 1 ;
    }
    if(!(st.st_mode & S_IFDIR)) {
        move(1,0) ;
        prints("Invalid Board Name\n") ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return 1 ;
    }
    sprintf(buf,"Select board '%s'",bname) ;
    report(buf) ;
    selboard = 1;
    strcpy(currboard, bname) ;
    move(0,0) ;
    clrtoeol() ;
    move(1,0) ;
    clrtoeol() ;
    return 0 ;
}

Zap()
{
    char bname[STRLEN] ;
    struct stat st ;
    char buf[512] ;
    int bnum ;
    struct fileheader binfo ;
    extern int numboards ;
    
    make_blist() ;
    move(0,0) ;
    prints("Zap a board from New searches\n") ;
    prints("Enter Name of Board: ") ;
    clrtoeol() ;
    namecomplete((char *)NULL,bname) ;
    if((*bname == '\0') || (stat(bfile(bname),&st) == -1)) {
        move(2,0) ;
        prints("Invalid Board Name") ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return 1 ;
    }
    if(!(st.st_mode & S_IFDIR)) {
        move(1,0) ;
        prints("Invalid Board Name\n") ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return 1 ;
    }
    if((bnum = getbnum(bname)) == 0) {
        move(1,0) ;
        prints("Something Weird just happened\n") ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return 1 ;
    }
    if(get_record(BOARDS,&binfo,sizeof(binfo),bnum) == -1) {
        move(2,0) ;
        prints("Invalid Board Number\n") ;
        pressreturn() ;
        clear() ;
        return 0 ;
    }
    binfo.accessed[usernum%MAXUSERS] ^= SHIFTMODE(usernum,FILE_READ) ;
    substitute_record(BOARDS,&binfo,sizeof(binfo),bnum) ;
    numboards = -1 ;
    move(0,0) ;
    clrtoeol() ;
    move(1,0) ;
    clrtoeol() ;
    return 0 ;
}


Post()
{
    struct fileheader postfile ;
    char fname[STRLEN] ;
    char *ip ;
    int fp ;
    char buf[512] ;
    extern struct user_info uinfo ;
    
    if(!selboard) {
        prints("\n\nUse (S)elect to select a board first.\n") ;
        pressreturn() ;
        clear() ;
        return 0 ;
    }
    bzero((void *)&postfile,sizeof(postfile)) ;
    SETFILEMODE(postfile.accessed,usernum,FILE_READ) ;
    SETFILEMODE(postfile.accessed,usernum,FILE_OWND) ;
    clear() ;
    prints("Posting message on Board '%s'\n\n",currboard) ;
    sprintf(fname,"M.%d.A",time(0)) ;
    sprintf(genbuf,"boards/%s/%s",currboard,fname) ;
    ip = rindex(fname,'A') ;
    while((fp = open(genbuf,O_CREAT|O_EXCL|O_WRONLY,0644)) == -1) {
        if(*ip == 'Z')
          ip++,*ip = 'A', *(ip + 1) = '\0' ;
        else
          (*ip)++ ;
        sprintf(genbuf,"boards/%s/%s",currboard,fname) ;
    }
    close(fp) ;
    strcpy(postfile.filename,fname) ;
    getdata(1,0,"Title: ",postfile.title,STRLEN,DOECHO,NULL) ;
    strncpy(postfile.owner,currentuser.username,STRLEN) ;
    sprintf(genbuf,"%s -pt \001%s\001 boards/%s/%s",EDITOR,
            postfile.title,currboard,postfile.filename) ;
    uinfo.mode = POSTING ;
    substitute_record(ULIST,(void *)&uinfo,sizeof(uinfo),utmpent) ;
    if(do_exec(genbuf,NULL) != 0) {
        sprintf(genbuf,"boards/%s/%s",currboard,fname) ;
        unlink(genbuf) ;
        clear() ;
        prints("File posting aborted\n") ;
        pressreturn() ;
        clear() ;
        return -1 ;
    }
    uinfo.mode = IDLE ;
    substitute_record(ULIST,(void *)&uinfo,sizeof(uinfo),utmpent) ;
    clear() ;
    if(append_record(strcat(bfile(currboard),DIR),&postfile,
                     sizeof(postfile)) == -1) {
        pressreturn() ;
        clear() ;
        return -1 ;
    }
    sprintf(buf,"posted '%s' on '%s'", postfile.title, currboard) ;
    report(buf) ;
    prints("File Posted\n") ;
    pressreturn() ;
    return 0 ;
}

ztest()
{
    pressreturn() ;
    return 0 ;
}

readtitle()
{
    prints("Interactive Read Menu                              Current Board '%s'\n",currboard) ;
    prints("(n)ext Message     (p)revious Message     (r)ead Message     (e)xit Read Menu\n");
    prints("##<cr> go to message ##      ($) go to last message     (h) Get a HELP screen\n");
    prints(" ENT   %-20s %s\n","Owner","Title") ;
    clrtobot() ;
}


char *
  readdoent(num,ent)
struct fileheader *ent ;
{
    static char buf[512] ;
    int type ;
    
    if(CHECKFILEMODE(ent->accessed,usernum,FILE_VISIT))
      type = 'U' ;
    else if(CHECKFILEMODE(ent->accessed,usernum,FILE_READ))
      type = ' ' ;
    else
      type = 'N' ;
    sprintf(buf," %3d %c %-20s %s",num, type, ent->owner, ent->title) ;
    return buf ;
}

char currfile[STRLEN] ;

cmpfilename(fhdr)
struct fileheader *fhdr ;
{
    if(!strncmp(fhdr->filename,currfile,STRLEN))
      return 1 ;
    return 0 ;
}

void
  updatefile(fhdr)
struct fileheader *fhdr ;
{
    SETFILEMODE(fhdr->accessed,usernum,FILE_READ) ;
    CLRFILEMODE(fhdr->accessed,usernum,FILE_VISIT) ;
}

read_post(ent,fileinfo,direct)
int ent ;
struct fileheader *fileinfo ;
char *direct ;
{
    char buf[512] ;
    char *t ;
    
    clear() ;
    strcpy(buf,direct) ;
    if (t = rindex(buf,'/'))
      *t = '\0' ;
    sprintf(genbuf,"%s/%s",buf,fileinfo->filename) ;
    more(genbuf,YEA) ;
    sprintf(genbuf,"Read '%s' on '%s'",fileinfo->title,currboard) ;
    report(genbuf) ; 
    strncpy(currfile,fileinfo->filename,STRLEN) ;
    update_file(direct,sizeof(struct fileheader),ent,cmpfilename,updatefile) ;
    return FULLUPDATE ;
}

do_select()
{
    char bname[STRLEN] ;
    struct stat st ;
    char buf[512] ;
    extern char dirbuf[4096] ;
    
    make_blist() ;
    move(0,0) ;
    prints("Select a Board Name (Be sure to include capital letters)\n") ;
    prints("Select Board: ") ;
    clrtoeol() ;
    namecomplete((char *)NULL,bname) ;
    if((*bname == '\0') || (stat(bfile(bname),&st) == -1)) {
        move(2,0) ;
        prints("Invalid Board Name") ;
        clrtoeol() ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return FULLUPDATE ;
    }
    if(!(st.st_mode & S_IFDIR)) {
        move(1,0) ;
        prints("Invalid Board Name") ;
        clrtoeol() ;
        pressreturn() ;
        move(0,0) ;
        clrtoeol() ;
        move(1,0) ;
        clrtoeol() ;
        move(2,0) ;
        clrtoeol() ;
        return FULLUPDATE ;
    }
    sprintf(buf,"read menu Select board '%s'",bname) ;
    report(buf) ;
    selboard = 1;
    strcpy(currboard, bname) ;
    move(0,0) ;
    clrtoeol() ;
    move(1,0) ;
    clrtoeol() ;
    strcpy(dirbuf,strcat(bfile(currboard),DIR)) ;
    return NEWDIRECT ;
}


do_post()
{
    struct fileheader postfile ;
    char fname[STRLEN] ;
    char *ip ;
    int fp ;
    char buf[512] ;
    extern struct user_info uinfo ;
    
    bzero(&postfile,sizeof(postfile)) ;
    SETFILEMODE(postfile.accessed,usernum,FILE_READ) ;
    SETFILEMODE(postfile.accessed,usernum,FILE_OWND) ;
    clear() ;
    prints("Posting message on Board '%s'\n\n",currboard) ;
    sprintf(fname,"M.%d.A",time(0)) ;
    sprintf(genbuf,"boards/%s/%s",currboard,fname) ;
    ip = rindex(fname,'A') ;
    while((fp = open(genbuf,O_CREAT|O_EXCL|O_WRONLY,0644)) == -1) {
        if(*ip == 'Z')
          ip++,*ip = 'A', *(ip + 1) = '\0' ;
        else
          (*ip)++ ;
        sprintf(genbuf,"boards/%s/%s",currboard,fname) ;
    }
    close(fp) ;
    strcpy(postfile.filename,fname) ;
    getdata(1,0,"Title: ",postfile.title,STRLEN,DOECHO,NULL) ;
    strncpy(postfile.owner,currentuser.username,STRLEN) ;
    sprintf(genbuf,"%s -pt \001%s\001 boards/%s/%s",EDITOR,postfile.title,
            currboard,postfile.filename) ;
    uinfo.mode = POSTING ;
    substitute_record(ULIST,(void *)&uinfo,sizeof(uinfo),utmpent) ;
    if(do_exec(genbuf,NULL) != 0) {
        sprintf(genbuf,"boards/%s/%s",currboard,fname) ;
        unlink(genbuf) ;
        clear() ;
        prints("File posting aborted...\n") ;
        pressreturn() ;
        clear() ;
        return FULLUPDATE ;
    }
    uinfo.mode = IDLE ;
    substitute_record(ULIST,(void *)&uinfo,sizeof(uinfo),utmpent) ;
    clear() ;
    if(append_record(strcat(bfile(currboard),DIR),&postfile,
                     sizeof(postfile)) == -1) {
        pressreturn() ;
        clear() ;
        bell() ;
        return FULLUPDATE ;
    }
    sprintf(buf,"posted '%s' on '%s'", postfile.title, currboard) ;
    report(buf) ;
    prints("File Posted\n") ;
    pressreturn() ;
    return FULLUPDATE ;
}


/*ARGSUSED*/
edit_post(ent,fileinfo,direct)
int ent ;
struct fileheader *fileinfo ;
char *direct ;
{
    char buf[512] ;
    char *t ;
    
    if((currentuser.userlevel < 255)) {
        bell() ;
        return DONOTHING ;
    }
    clear() ;
    strcpy(buf,direct) ;
    if (t = rindex(buf,'/'))
      *t = '\0' ;
    sprintf(genbuf,"%s %s/%s",EDITOR,buf,fileinfo->filename) ;
    do_exec(genbuf,NULL) ;
    strncpy(currfile,fileinfo->filename,STRLEN) ;
    return FULLUPDATE ;
}

del_range(ent,fileinfo,direct)
int ent ;
struct fileheader *fileinfo ;
char *direct ;
{
    char num1[10],num2[10] ;
    int inum1, inum2 ;
    
    if(currentuser.userlevel < 255) {
        bell() ;
        return DONOTHING ;
    }
    clear() ;
    prints("Delete RANGE\n") ;
    getdata(1,0,"Enter Low End of Range (inclusive): ",num1,10,DOECHO,NULL) ;
    inum1 = atoi(num1) ;
    if(inum1 <= 0) {
        prints("Invalid Low range number\n") ;
        pressreturn() ;
        return FULLUPDATE ;
    }
    getdata(2,0,"Enter High End of Range (inclusive): ",num2,10,DOECHO,NULL) ;
    inum2 = atoi(num2) ;
    if(inum2 <= inum1) {
        prints("Invalid High range number\n") ;
        pressreturn() ;
        return FULLUPDATE ;
    }
    getdata(3,0,"Are You Sure?  (Yes, or No): ",num1,10,DOECHO,NULL) ;
    if(!strcmp(num1,"Yes") || !strcmp(num1,"yes")) {
        delete_range(direct,inum1,inum2) ;
        prints("Delete Complete\n") ;
        pressreturn() ;
        return FULLUPDATE ;
    }
    prints("Delete Aborted\n") ;
    pressreturn() ;
    return FULLUPDATE ;
}

del_post(ent,fileinfo,direct)
int ent ;
struct fileheader *fileinfo ;
char *direct ;
{
    char buf[512] ;
    char *t ;
    
    if((currentuser.userlevel < 255) && 
       !CHECKFILEMODE(fileinfo->accessed,usernum,FILE_OWND)) {
        bell() ;
        return DONOTHING ;
    }
    clear() ;
    prints("Delete Message '%s'.",fileinfo->title) ;
    getdata(1,0,"(Yes, or No): ",genbuf,STRLEN,DOECHO,NULL) ;
    if(strcmp(genbuf,"Yes") && strcmp(genbuf,"yes")) { /* if not yes quit */
        move(2,0) ;
        prints("Quiting Delete Post\n") ;
        pressreturn() ;
        clear() ;
        return FULLUPDATE ;
    }		
    strcpy(buf,direct) ;
    if (t = rindex(buf,'/'))
      *t = '\0' ;
    sprintf(genbuf,"Deleted '%s' on '%s'",fileinfo->title,currboard) ;
    report(genbuf) ; 
    strncpy(currfile,fileinfo->filename,STRLEN) ;
    if(!delete_file(direct,sizeof(struct fileheader),ent,cmpfilename)) {
        sprintf(genbuf,"%s/%s",buf,fileinfo->filename) ;
        unlink(genbuf) ;
        return FULLUPDATE ;
    }
    move(2,0) ;
    prints("Delete failed\n") ;
    pressreturn() ;
    clear() ;
    return FULLUPDATE ;
}

static int sequent_ent ;

sequent_messages(fptr)
struct fileheader *fptr ;
{
    static int idc ;
    
    if(fptr == NULL) {
        idc = 0 ;
        return 0 ;
    }
    idc++ ;
    if(idc < sequent_ent)
      return 0;
    if(CHECKFILEMODE(fptr->accessed,usernum,FILE_READ) &&
       !CHECKFILEMODE(fptr->accessed,usernum,FILE_VISIT))
      return 0 ;
    mot = 1 ;
    prints("Read Message on board '%s' entitled:\n\"%s\" posted by %s.\n",
           currboard,fptr->title,fptr->owner) ;
    getdata(3,0,"(Yes or No or Quit): ",genbuf,5,DOECHO,NULL) ;
    if(genbuf[0] != 'y' && genbuf[0] != 'Y' && genbuf[0] != '\0') {
        if(genbuf[0] == 'q' || genbuf[0] == 'Q') {
            clear() ;
            return QUIT ;
        }
        clear() ;
        return 0;
    }
    sprintf(genbuf,"boards/%s/%s",currboard,fptr->filename) ;
    more(genbuf,YEA) ;
    clear() ;
    sprintf(genbuf,"boards/%s/%s",currboard,DIR) ;
    strncpy(currfile,fptr->filename,STRLEN) ;
    update_file(genbuf,sizeof(struct fileheader),idc,cmpfilename,updatefile) ;
    return 0 ;
}

/*ARGSUSED*/
sequential_read(ent,fileinfo,direct)
int ent ;
struct fileheader *fileinfo ;
char *direct ;
{
    clear() ;
    sequent_messages((struct fileheader *)NULL) ;
    sequent_ent = ent ;
    quiting = NA ;
    apply_record(strcat(bfile(currboard),DIR),sequent_messages,sizeof(struct fileheader)) ;
    return FULLUPDATE ;
}

extern int mainreadhelp() ;

struct one_key  read_comms[] = {
    'r',       read_post,
    'd',       del_post,
    'D',       del_range,
    'E',       edit_post,
    's',       do_select,
    0x10,      do_post, /* CTRL(P) */
    'S',       sequential_read,
    'h',       mainreadhelp,
    0x0a,      mainreadhelp, /* CTRL(J) */
    '\0',      NULL
  } ;


Read()
{
    extern int talkrequest ;
    
    if(!selboard) {
        move(2,0) ;
        prints("You must select a board first (Use (S)elect)\n") ;
        pressreturn() ;
        move(2,0) ;
        clrtoeol() ;
        return -1 ;
    }
    i_read(strcat(bfile(currboard),DIR),readtitle,readdoent,&read_comms[0]) ;
    return 0 ;
}

Goodbye()
{
    extern int started ;
    clear() ;
    refresh() ;
    reset_tty() ;
    printf("Goodbye!\n") ;
    report("exit") ;
    if(started)
      if((ufp = fopen("usies","a")) != NULL) {
          fcntl(fileno(ufp),F_WRLCK) ;
          ti = time(0) ;
          fprintf(ufp,"EXIT  %-10s %-20s %s",currentuser.userid,currentuser.username,ctime(&ti)) ;
          fflush(ufp) ;
          u_exit() ;
          fcntl(fileno(ufp),F_UNLCK) ;
          fclose(ufp) ;
      }
    
    exit(0) ;
}

report(s)
char *s ;
{
    static int disable = NA ;
    int fd ;
    
    if(disable)
      return ;
    if((fd = open("trace",O_WRONLY,0644)) != -1 ) {
        char buf[512] ;
        fcntl(fd,F_WRLCK) ;
        lseek(fd,0,L_XTND) ;
        sprintf(buf,"%s %s\n",currentuser.userid,s) ;
        write(fd,buf,strlen(buf)) ;
        fcntl(fd,F_UNLCK) ;
        close(fd) ;
        return ;
    }
    disable = YEA ;
    return ;
}

char saveboard[STRLEN] ;

int tuid ;
int tfile, rfile ;


get_messages(fptr)
struct fileheader *fptr ;
{
    if(CHECKFILEMODE(fptr->accessed,tuid,FILE_READ))
      rfile++ ;
    tfile++ ;
    return 0 ;
}


get_boards(fptr)
struct fileheader *fptr ;
{
    char buf[512] ;
    sprintf(buf,"boards/%s/%s",fptr->filename,DIR) ;
    apply_record(buf,get_messages,sizeof(struct fileheader)) ;
    return 0 ;
}

get_users(uptr)
struct user *uptr ;
{
    struct user u ;
    char tbuf[STRLEN] ;
    
    bcopy(uptr,&u,sizeof(u)) ;
    tfile = 0 ;
    rfile = 0 ;
    if(*(uptr->userid) == '\0') {
        tuid++ ;
        return 0 ;
    }
    
    strcpy(tbuf,ctime(&u.lastlogin)) ;
    strncpy(genbuf,tbuf+4,7) ;
    genbuf[7] = '\0' ;
    strcat(genbuf,tbuf+22) ;
    printf("%-10s %-19s %-9s %-4d %6s  %s",u.userid, u.username,
           u.termtype, u.userlevel, NameProtocol(u.protocol), genbuf) ;
    tuid++ ;
    return 0 ;
}



UserStatus()
{
    tuid = 1 ;
    printf("%-10s %-19s %-9s %-4s %6s  %s\n","USER ID", "USERNAME",
           "TERMTYPE", "LEVL", "PROTO", "LAST LOGIN") ;
    if(apply_record(PASSFILE, get_users, sizeof(struct user)) == -1) {
        fprintf(stderr,"Error in apply_record\n") ;
        exit(-1) ;
    }
    return 0 ;
}

int m_count ;
int m_total ;

char atot[MAXUSERS] ;

g_messages(fptr)
struct fileheader *fptr ;
{
    int i ;
    int count = 0 ;
    
    printf("File: %s\n\n",fptr->title) ; 
    for(i = 1; i < MAXUSERS; i++)
      if(CHECKFILEMODE(fptr->accessed,i,FILE_READ)) {
          struct user us ;
          atot[i] = 1 ;
          get_record(PASSFILE,&us,sizeof us, i) ;
          count++ ;
          printf("%s ",us.userid) ;
      }
    m_count++ ;
    m_total += count ;
    printf("\nCount = %d\n",count) ;
    return 0 ;
}


g_boards(fptr,mode)
struct fileheader *fptr ;
int mode ;
{
    char buf[512] ;
    int i,total ;
    sprintf(buf,"boards/%s/%s",fptr->filename,DIR) ;
    printf("------------------------------------------Board: %s\n\n",fptr->filename) ;
    bzero(atot,sizeof(atot)) ;
    m_count = 0 ;
    m_total = 0 ;
    apply_record(buf,g_messages,sizeof(struct fileheader)) ;
    printf("AVG Read = %f\n",(double)m_total/(double)m_count) ;
    total = 0 ;
    for(i=1;i<MAXUSERS;i++) {
        if(atot[i])
          total++ ;
    }
    printf("DVRS Read = %d\n",total) ;
    return 0 ;
}




BoardStatus()
{
    apply_boards(g_boards) ;
}

Info()
{
    more("Version.Info",YEA) ;
    clear() ;
    return 0 ;
}

Conditions()
{
    more("COPYING",YEA) ;
    clear() ;
    return 0 ;
}

