/*
    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 <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#ifndef XINU
#include <sys/stat.h>
#endif

#define BUFSIZE 4096


append_record(filename,record,size)
char *filename ;
char *record ;
int size ;
{
	int fd ;
	if((fd = open(filename,O_WRONLY|O_APPEND|O_CREAT,0644)) == -1) {
		perror("open") ;
		return -1 ;
	}
	fcntl(fd,F_WRLCK) ;
	if(write(fd,record,size) == -1) {
		perror("write") ;
		return -1 ;
	}
	fcntl(fd,F_UNLCK) ;
	close(fd) ;
	return 0 ;
}

static char gb[BUFSIZE] ;

apply_record(filename,fptr,size)
char *filename ;
int (*fptr)() ;
int size ;
{
	int fd ;

	if(size > BUFSIZE) {
		fprintf(stderr,"size to big in apply record\n") ;
		return -1 ;
	}
	if((fd = open(filename,O_RDONLY,0)) == -1)
	  return -1 ;
	while(read(fd,gb,size) == size)
	  if((*fptr)(gb) == QUIT) {
          close(fd) ;
          return QUIT ;
      }
	close(fd) ;
	return 0 ;
}

search_record(filename,rptr,size,fptr,farg)
char *filename ; 
char *rptr ;
int size ;
int (*fptr)() ;
int farg ;
{
	int fd ;
	int id = 1 ;

	if((fd = open(filename,O_RDONLY,0)) == -1)
	  return 0 ;
	while(read(fd,rptr,size) == size) {
		if((*fptr)(farg,rptr)) {
			close(fd) ;
			return id ;
		}
		id++ ;
	}
	close(fd) ;
	return 0 ;
}

get_record(filename,rptr,size,id)
char *filename ;
char *rptr ;
int size, id ;
{
	int fd ;

	if((fd = open(filename,O_RDONLY,0)) == -1)
	  return -1 ;
	if(lseek(fd,size*(id-1),L_SET) == -1) {
		close(fd) ;
		return -1 ;
	}
	if(read(fd,rptr,size) != size) {
		close(fd) ;
		return -1 ;
	}
	close(fd) ;
	return 0 ;
}

long
get_num_records(filename,size)
char *filename ;
{
	struct stat st ;

	if(stat(filename,&st) == -1)
	  return 0 ;
	return (st.st_size/size) ;
}

int
get_records(filename,rptr,size,id,number)
char *filename ;
char *rptr ;
int size, id, number ;
{
	int fd ;
	int n ;

	if((fd = open(filename,O_RDONLY,0)) == -1)
	  return -1 ;
	if(lseek(fd,size*(id-1),L_SET) == -1) {
		close(fd) ;
		return 0 ;
	}
	if((n = read(fd,rptr,size*number)) == -1) {
		close(fd) ;
		return -1 ;
	}
	close(fd) ;
	return (n/size) ;
}

substitute_record(filename,rptr,size,id)
char *filename ;
char *rptr ;
int size, id ;
{
	int fd ;
	if((fd = open(filename,O_WRONLY|O_CREAT,0644)) == -1)
	  return -1 ;
	fcntl(fd,F_WRLCK) ;
	if(lseek(fd,size*(id-1),L_SET) == -1) {
		close(fd) ;
		return -1 ;
	}
	if(write(fd,rptr,size) != size) {
		close(fd) ;
		return -1 ;
	}
	fcntl(fd,F_UNLCK) ;
	close(fd) ;
	return 0 ;
}

delete_record(filename,size,id)
char *filename ;
int size, id ;
{
	int fdr, fdw ;
	int fd ;
	int count ;
	
	if((fd = open(".dellock",O_RDWR|O_CREAT|O_APPEND, 0644)) == -1)
		return -1 ;
	fcntl(fd,F_WRLCK) ;

	if((fdr = open(filename,O_RDONLY,0)) == -1) {
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		return -1 ;
	}
	if((fdw = open(".tmpfile",O_WRONLY|O_CREAT|O_EXCL,0644)) == -1) {
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		close(fdr) ;
		return -1 ;
	}
	count = 1 ;
	while(read(fdr,gb,size) == size)
		if(id != count++ && (write(fdw,gb,size) == -1)) {
			unlink(".tmpfile") ;
			close(fdr) ;
			close(fdw) ;
			fcntl(fd,F_UNLCK) ;
			close(fd) ;
			return -1 ;
		}
	close(fdr) ;
	close(fdw) ;
	if(rename(filename,".deleted") == -1) {
        unlink(".tmpfile") ;
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		return -1 ;
	}
	if(rename(".tmpfile",filename) == -1) {
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		return -1 ;
	}
	fcntl(fd,F_UNLCK) ;
	close(fd) ;
	return 0 ;
}

delete_range(filename,id1,id2)
char *filename ;
int id1,id2 ;
{
	int fdr, fdw ;
	int fd ;
	int count ;
    struct fileheader fhdr ;

	if((fd = open(".dellock",O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
        perror("open .dellock") ;
		return -1 ;
    }
	fcntl(fd,F_WRLCK) ;
	if((fdr = open(filename,O_RDONLY,0)) == -1) {
        perror("open failed") ;
        fprintf(stderr,"dir = %s\n",filename) ;
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		return -1 ;
	}
	if((fdw = open(".tmpfile",O_WRONLY|O_CREAT|O_EXCL,0644)) == -1) {
        perror("open .tmpfile") ;
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		close(fdr) ;
		return -1 ;
	}
	count = 1 ;
	while(read(fdr,&fhdr,sizeof fhdr) == sizeof fhdr) {
        if(count < id1 || count > id2) {
            if((write(fdw,&fhdr,sizeof fhdr) == -1)) {
                unlink(".tmpfile") ;
                close(fdr) ;
                close(fdw) ;
                fcntl(fd,F_UNLCK) ;
                close(fd) ;
                return -1 ;
            }
        } else {
            char *t ;
            char buf[256] ;
            strcpy(buf,filename) ;
            if(t = rindex(buf,'/'))
              *t = '\0' ;
            sprintf(gb,"%s/%s",buf,fhdr.filename) ;
            unlink(gb) ;
        }
        count++ ;
    }
	close(fdr) ;
	close(fdw) ;
	if(rename(filename,".deleted") == -1) {
        perror("rename .deleted") ;
        unlink(".tmpfile") ;
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		return -1 ;
	}
	if(rename(".tmpfile",filename) == -1) {
        perror("rename .tmpfile") ;
		fcntl(fd,F_UNLCK) ;
		close(fd) ;
		return -1 ;
	}
	fcntl(fd,F_UNLCK) ;
	close(fd) ;
	return 0 ;
}

char abuf[4096] ;

int
update_file(dirname,size,ent,filecheck,fileupdate)
char *dirname ;
int size,ent ;
int (*filecheck)() ;
void (*fileupdate)() ;
{
	int fd ;

	if((fd = open(dirname,O_RDWR)) == -1)
	  return -1 ;
	fcntl(fd,F_WRLCK) ;
	if(lseek(fd,size*(ent-1),L_SET) != -1) {
		if(read(fd,abuf,size) == size)
		  if((*filecheck)(abuf)) {
			  lseek(fd,-size,L_INCR) ;
			  (*fileupdate)(abuf) ;
			  if(write(fd,abuf,size) != size) {
				  fcntl(fd,F_UNLCK) ;
				  close(fd) ;
				  return -1 ;
			  }
			  fcntl(fd,F_UNLCK) ;
			  close(fd) ;
			  return 0 ;
		  }
	}
	lseek(fd,0,L_SET) ;
	while(read(fd,abuf,size) == size) {
		if((*filecheck)(abuf)) {
			lseek(fd,-size,L_INCR) ;
			(*fileupdate)(abuf) ;
			if(write(fd,abuf,size) != size) {
				fcntl(fd,F_UNLCK) ;
				close(fd) ;
				return -1 ;
			}
			fcntl(fd,F_UNLCK) ;
			close(fd) ;
			return 0 ;
		}
	}
	fcntl(fd,F_UNLCK) ;
	close(fd) ;
	return -1 ;
}

delete_file(dirname,size,ent,filecheck)
char *dirname ;
int size,ent ;
int (*filecheck)() ;
{
	int fd ;
	struct stat st ;
	long numents ;

	if((fd = open(dirname,O_RDWR)) == -1)
	  return -1 ;
	fcntl(fd,F_WRLCK) ;
	fstat(fd,&st) ;
	numents = ((long)st.st_size)/size ;
	if(((long)st.st_size) % size != 0)
	  fprintf(stderr,"Irregular File Size, Truncating\n") ;
	if(lseek(fd,size*(ent-1),L_SET) != -1) {
		if(read(fd,abuf,size) == size)
		  if((*filecheck)(abuf)) {
			  int i ;
			  for(i = ent; i < numents; i++) {
				  if(lseek(fd,(i)*size,L_SET) == -1)
					break ;
				  if(read(fd,abuf,size) != size)
					break ;
				  if(lseek(fd,(i-1)*size,L_SET) == -1)
					break ;
				  if(write(fd,abuf,size) != size)
					break ;
			  }
			  ftruncate(fd,size*(numents-1)) ;
			  fcntl(fd,F_UNLCK) ;
			  close(fd) ;
			  return 0 ;
		  }
	}
	lseek(fd,0,L_SET) ;
	ent = 1 ;
	while(read(fd,abuf,size) == size) {
		if((*filecheck)(abuf)) {
			int i ;
			for(i = ent; i < numents; i++) {
				if(lseek(fd,(i+1)*size,L_SET) == -1)
					break ;
				if(read(fd,abuf,size) != size)
				  break ;
				if(lseek(fd,(i)*size,L_SET) == -1)
				  break ;
				if(write(fd,abuf,size) != size)
				  break ;
			}
			ftruncate(fd,size*(numents-1)) ;
			fcntl(fd,F_UNLCK) ;
			close(fd) ;
			return 0 ;
		}
		ent++ ;
	}
	fcntl(fd,F_UNLCK) ;
	close(fd) ;
	return -1 ;
}

