/* $Id$ */
#include "bbs.h"

static int      semid = -1;

#ifndef SEM_R
#define SEM_R 0400
#endif

#ifndef SEM_A
#define SEM_A 0200
#endif

#ifndef __FreeBSD__
union semun {
    int             val;	/* value for SETVAL */
    struct semid_ds *buf;	/* buffer for IPC_STAT & IPC_SET */
    u_short        *array;	/* array for GETALL & SETALL */
    struct seminfo *__buf;	/* buffer for IPC_INFO */
};
#endif

int
passwd_init()
{
    semid = semget(PASSWDSEM_KEY, 1, SEM_R | SEM_A | IPC_CREAT | IPC_EXCL);
    if (semid == -1) {
	if (errno == EEXIST) {
	    semid = semget(PASSWDSEM_KEY, 1, SEM_R | SEM_A);
	    if (semid == -1) {
		perror("semget");
		exit(1);
	    }
	} else {
	    perror("semget");
	    exit(1);
	}
    } else {
	union semun     s;

	s.val = 1;
	if (semctl(semid, 0, SETVAL, s) == -1) {
	    perror("semctl");
	    exit(1);
	}
    }

    return 0;
}

int
passwd_update_money(int num)
/* update money only 
   Ptt: don't call it directly, call deumoney() */
{
   userec_t user;
   int pwdfd, money = moneyof(num);
   char path[256];

   if (num < 1 || num > MAX_USERS)
	return -1;

   sethomefile(path, getuserid(num), ".passwd");

   if ((pwdfd = open(path, O_WRONLY)) < 0)
       {
        if(passwd_index_query(num, &user)<0)  // tempory code, will be removed
               exit(1);
        user.money=money;
        passwd_update(num, &user);
        return 0;
       }

   if(lseek(pwdfd, (off_t)((int)&user.money - (int)&user), SEEK_SET) >= 0)
              write(pwdfd, &money, sizeof(int));

   close(pwdfd);
   return 0;
}

int
passwd_query_money(int num)
/* query money only 
   Ptt: don't call it directly, call moneyof() */
{
   userec_t user;
   int pwdfd, money;
   char path[256];

   if (num < 1 || num > MAX_USERS)
        return -1;

   sethomefile(path, getuserid(num), ".passwd");

   if ((pwdfd = open(path, O_RDONLY)) < 0)
       {
        if(passwd_index_query(num, &user)<0)  // tempory code, will be removed
                  return 0;
        return user.money;
       }
   if(lseek(pwdfd, (off_t)((int)&user.money - (int)&user), SEEK_SET) >= 0)
              if(read(pwdfd, &money, sizeof(int))==-1)
                  money=0;
   close(pwdfd);
   if(money<0)
      {
        if(passwd_index_query(num, &user)<0)  // tempory code, will be removed
                  return 0;
        return user.money;
      }

   return money;
}

int
passwd_index_update(int num, userec_t * buf)
{
    int  pwdfd;
    if (num < 1 || num > MAX_USERS)
	return -1;
    buf->money = moneyof(num);
    if ((pwdfd = open(fn_passwd, O_WRONLY)) < 0)
	exit(1);
    lseek(pwdfd, sizeof(userec_t) * (num - 1), SEEK_SET);
    write(pwdfd, buf, sizeof(userec_t));
    close(pwdfd);
    return 0;
}

int
passwd_update(int num, userec_t * buf)
{
   int pwdfd;
   char path[256];

   if(!buf->userid[0]) return -1;

   sethomefile(path, buf->userid, ".passwd");
   buf->money = moneyof(num);
   if ((pwdfd = open(path, O_WRONLY|O_CREAT, 0600)) < 0)
           return -1;
   write(pwdfd, buf, sizeof(userec_t));
   close(pwdfd);
   return 0;
}

int
passwd_index_query(int num, userec_t * buf)
{
    int             pwdfd;
    if (num < 1 || num > MAX_USERS)
	return -1;
    if ((pwdfd = open(fn_passwd, O_RDONLY)) < 0)
	exit(1);
    lseek(pwdfd, sizeof(userec_t) * (num - 1), SEEK_SET);
    read(pwdfd, buf, sizeof(userec_t));
    close(pwdfd);
    return 0;
}

userec_t userecbuf;
int initcuser(char *userid)
{
    // Ptt: setup cuser and usernum here
   cuser = &userecbuf;
   if(userid[0]=='\0' ||
   !(usernum = searchuser(userid)) || usernum > MAX_USERS)
      return -1;
   passwd_query(usernum, &userecbuf);
   return usernum;
}

int
passwd_query(int num, userec_t * buf)
{
   int pwdfd;
   char path[256], *userid;

   if (num < 1 || num > MAX_USERS)
        return -1;
   userid = getuserid(num); 

   if(userid[0]=='\0') return -1;

   sethomefile(path, userid, ".passwd");
   if((pwdfd = open(path, O_RDONLY)) < 0)
     {  // copy from index // tempory code, will be removed
        if(passwd_index_query(num, buf)<0)
               exit(1);
        passwd_update(num, buf);
        return 0;
     }
   read(pwdfd, buf, sizeof(userec_t));
   close(pwdfd);
   return 0;
}

int
passwd_apply(int (*fptr) (int, userec_t *))
{
    int             i;
    userec_t        user;
    for (i = 0; i < MAX_USERS; i++) {
	passwd_query(i + 1, &user);
	if ((*fptr) (i, &user) == QUIT)
	    return QUIT;
    }
    return 0;
}

void
passwd_lock()
{
    struct sembuf   buf = {0, -1, SEM_UNDO};

    if (semop(semid, &buf, 1)) {
	perror("semop");
	exit(1);
    }
}

void
passwd_unlock()
{
    struct sembuf   buf = {0, 1, SEM_UNDO};

    if (semop(semid, &buf, 1)) {
	perror("semop");
	exit(1);
    }
}