*****Listing 4*****

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#define PATHDLM	('/')	/* path name delimiter */
#include "rwsem.h"

/* function declarations */
int getcnt(char *file, int *cntp);
int putcnt(char *file, int cnt);

/* read/write semaphore set table definition */
static rwsset_t rwst[RWSOPEN_MAX];

/* rwslock:  read/write semaphore lock */
int rwslock(rwsset_t *rwsp, int rwsno, int ltype)
{
	int wsem = 0;			/* write semaphore */
	int rsem = 0;			/* read semaphore */
	int rc = 0;			/* readcount */
	char rcpath[PATH_MAX + 1];	/* readcount file path name */

	/* identify write and read semaphores and read-count file */
	wsem = rwsno * 2;
	rsem = wsem + 1;
	sprintf(rcpath, "%s%cr%d", rwsp->rwsdir, (int)PATHDLM, rwsno);

	switch (ltype) {
	case RWS_UNLCK:	/* unlock */
		switch (rwsp->lockheld[rwsno]) {
		case RWS_UNLCK:	/* unlock */
			break;	/* case RWS_UNLCK: */
		case RWS_RDLCK:	/* read lock */
			if (semlower(rwsp->ssp, rsem) == -1) {	/* allocate readcount */
				return -1;
			}
			getcnt(rcpath, &rc);			/* get readcount */
			rc--;					/* decrement readcount */
			if (rc == 0) {				/* if no other readers, */
				if (semraise(rwsp->ssp, wsem) == -1) {	/* free resource */
					semraise(rwsp->ssp, rsem);
					return -1;
				}
			}
			putcnt(rcpath, rc);			/* store new readcount */
			if (semraise(rwsp->ssp, rsem) == -1) {	/* free readcount */
				return -1;
			}
			break;	/* case RWS_RDLCK: */
		case RWS_WRLCK:	/* write lock */
			if (semraise(rwsp->ssp, wsem) == -1) {
				return -1;
			}
			break;	/* case RWS_WRLCK: */
		default:
			errno = RWSPANIC;
			return -1;
			break;	/* default: */
		};
		break;	/* case RWS_UNLCK: */
	case RWS_RDLCK:	/* read lock */
		if (rwsp->lockheld[rwsno] == RWS_RDLCK) {
			errno = 0;
			return 0;
		}
		if (semlower(rwsp->ssp, rsem) == -1) {	/* allocate readcount */
			return -1;
		}
		getcnt(rcpath, &rc);			/* get readcount */
		rc++;					/* increment readcount */
		if (rc == 1) {				/* if no other readers, */
			if (semlower(rwsp->ssp, wsem) == -1) {	/* allocate resource */
				semraise(rwsp->ssp, rsem);
				return -1;
			}
		}
		putcnt(rcpath, rc);			/* store new readcount */
		if (semraise(rwsp->ssp, rsem) == -1) {	/* free readcount */
			if (rc == 1) semraise(rwsp->ssp, wsem);
			return -1;
		}
		break;	/* case RWS_RDLCK: */
	case RWS_WRLCK:	/* write lock */
		if (semlower(rwsp->ssp, wsem) == -1) {	/* allocate resource */
			return -1;
		}
		break;	/* case RWS_WRLCK: */
	default:
		errno = EINVAL;
		return -1;
		break;	/* default: */
	};

	/* save type of lock held */
	rwsp->lockheld[rwsno] = ltype;

	errno = 0;
	return 0;
}

