#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include <errno.h>
#include <stdio.h>
#include <signal.h>

extern int errno;

#define SHMKEY (key_t) IPC_PRIVATE		  /* shared memory key */
#define SKEY   (key_t) IPC_PRIVATE		  /* semaphore key  */
#define SEMPERM 0600				  /* permission for sem */
#define FULL 0					  /* sempahore: full */
#define EMPTY 1					  /* semaphore: empty */
#define MUTEX 2					  /* semaphore: mutex */
#define BUFSIZE 10				  /* buffer size */

/* will hold data and read count */

char buffer[BUFSIZE];


#define IFLAGS (SEMPERM | IPC_CREAT)
#define ERR ((char *) -1)

static int shmid, semid;

/* create and attach shared memory segments */

int getseg(char **buf)
{

    /* create shared memory segments */
    if ((shmid = shmget(SHMKEY, BUFSIZE, IFLAGS)) < 0) {
	perror("shmget failed");
	return(-1);
    }	

    /* attach shared memory segments */
    if ((*buf = (char *) shmat(shmid, 0, 0)) == ERR) {
	perror("shmat failed");
	return(-1);
    }	
}

/* initsem -- semaphore initialization */

int initsem(key_t semkey)
{
    int status = 0;				  /* ret val from init. */
    union semun {
      int val;
      struct semid_ds *stat;
      ushort *array;
    } ctl_arg;

    if ((semid = semget(semkey, 3, IFLAGS))>0) {
	    /* if ok, initialize the semaphores */
	    ushort array[3] = {BUFSIZE, 0, 1};
	    ctl_arg.array = array;
	    status = semctl(semid, 0, SETALL, ctl_arg);
    }


    if (semid == -1 || status == -1) {
	perror("initsem failed");
	return(-1);
    } else
	return(semid);
}

    
/* remove shared memory identifiers and sem set id */
int ipcremove()
{
    if (shmctl(shmid, IPC_RMID, NULL) < 0) {
	perror("remove_shm failed");
	return(-1);
    } 
    if (semctl(semid, 0, IPC_RMID) < 0) {
	perror("remove_sem failed");
	return(-1);
    }
}	

/* p -- semaphore p operation */
/* We use 'p' here, rather than more understandable 'wait', since the latter */
/* can be confused with the Unix 'wait'. */

int p(int semid, int semnum)
{

    struct sembuf p = {0,  -1, 0};
   
    p.sem_num=semnum;	
    if (semop(semid, &p, 1) == -1) {
	fprintf(stderr,"p(%d) failed",semnum);
	perror("");
	exit(1);
    } 
    else {
	return(0);
    }
}
   

/* v -- semaphore v operation */

int v(int semid, int semnum)
{

    struct sembuf v={0, 1, 0};	

    v.sem_num=semnum;	
    if (semop(semid, &v, 1) == -1) {
	fprintf(stderr,"v(%d) failed",semnum);
	perror ("");
	exit(1);
    } 
    else {
	return(0);
    }
}


/* consumer -- consumer program using a bounded buffer implemented */
/* with semaphores: get a character from the buffer and print it out */
int consumer(int semid, char *buffer)
{

    char nextc;
    int out = 0;

    for (;;) {
	p(semid, FULL);				  /* if any data, */
	p(semid, MUTEX);
	nextc = buffer[out];			  /* get it */
	out = (out + 1) % BUFSIZE;
	v(semid, MUTEX);
	v(semid, EMPTY);			  /* signal buffer has space */
	printf("%c", toupper(nextc));
	fflush(stdout);
    }
}


/* producer -- read in a character from stdin and place it in the buffer */
int producer(int semid, char *buffer)
{

    char nextp;
    int in = 0, n;

    for (;;) { 
	/* read a character */
	if ((n=read(0,&nextp,sizeof(nextp)))<=0) {
	    if (n<0) perror("read");
	    break;				  /* if EOF, finish it */	 
	}
	p(semid, EMPTY);			  /* if any space, */
	p(semid, MUTEX);
	buffer[in] = nextp;			  /* add it to buffer */
	in = (in + 1) % BUFSIZE;
	v(semid, MUTEX);
	v(semid, FULL);				  /* signal buffer has data */
    }
}


/* simple server for a semaphore server */

main()
{
    int semid;
    int pid;
    char *buf;

    if ((semid = initsem(SKEY)) < 0)		  /* create a semaphore set */
	exit(1);

    getseg(&buf);				  /* create & attach shm */

    switch (pid = fork()) {
    case -1:
	perror("fork");
	break;
    case 0:					  /* child */
	/* break link with terminal */
	consumer(semid, buf);
	break;					  /* actually, never exits */
    default:					  /* parent */
	producer(semid, buf);
	kill(pid,SIGTERM);
	waitpid(pid,NULL,0);
	ipcremove();
	break;
    }
    exit(pid != -1 ? 0 : 1);
}