/* **************************************
 * *                                    *
 * *   Message Queue Support Routines   *
 * *                                    *
 * **************************************
 * File: libs/user/msgque.c
 *
 *                        COPYRIGHT (c) 1999  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include "sigtypes.h"
#include "user_lib.h"

#define  MAXBUFSIZE  512
#define  MAXMSGSIZE  MAXBUFSIZE - sizeof(UINT4)

struct msgquebuf {
  long mtype ;
  char buf[MAXBUFSIZE] ;
} ;

/* ------------------------------
 * Routines for creating or locating a message queue.  The "Create"
 * routines will try to create the queue, and the "Find" routines will
 * try to attach to an existing queue.
 */
MESSAGE msgQueCreate(UINT4 *imsgqid_a, const char *sname_a)
{
  key_t   key ;
  MESSAGE istatus ;
  int     imsgqid ; 

  istatus = sig_ftok( sname_a, (SINT4 *)&key ) ;

  if( istatus == SS_NORMAL ) 
    {
      imsgqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666) ;
      if (imsgqid == -1)
	istatus = errno_to_message_va( errno, 1, sname_a ) ;
      else 
	{
	  istatus = SS_NORMAL ;
	  *imsgqid_a = imsgqid ;
	}
    }
  return istatus ;
}

MESSAGE msgQueCreateFind(UINT4 *imsgqid_a, const char *sname_a)
{
  key_t   key ;
  MESSAGE istatus ;
  int     imsgqid ; 

  istatus = sig_ftok( sname_a, (SINT4 *)&key) ;

  if( istatus == SS_NORMAL ) 
    {
      imsgqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666) ;
      if (imsgqid == -1)
	{
	  imsgqid = msgget(key, 0) ;
	  if (imsgqid == -1)
            istatus = errno_to_message_va( errno, 1, sname_a ) ;
	  else 
	    {
	      istatus = SS_NORMAL ;
	      *imsgqid_a = imsgqid ;
	    }
	}
      else 
	{
	  istatus = SS_NORMAL ;
	  *imsgqid_a = imsgqid ;
	}
    }
  return istatus ;
}

MESSAGE msgQueFind(UINT4 *imsgqid_a, const char *sname_a)
{
  key_t   key ;
  MESSAGE istatus ;
  int     imsgqid ; 

  istatus = sig_ftok( sname_a, (SINT4 *)&key ) ;

  if( istatus == SS_NORMAL ) 
    {
      imsgqid = msgget(key, 0) ;
      if (imsgqid == -1)
	istatus = errno_to_message_va( errno, 1, sname_a ) ;
      else 
	{
	  istatus = SS_NORMAL ;
	  *imsgqid_a = imsgqid ;
	}
    }
  return istatus ;
}

/* ------------------------------
 * Send and receive routines for NULL terminated strings.  The process
 * will block if there is no room in the queue during sending, or if
 * there are no available messages during receiving.
 *
 * WARNING: msgQueRecv() is not thread safe, because it returns a
 *          pointer to a static variable.  this could be fixed by
 *          changing its calling conventions.
 */
MESSAGE msgQueSend(UINT4 imsgqid_a, const char *send_str)
{
  int     str_len ;
  MESSAGE istatus ; struct msgquebuf msgqbuf ;

  msgqbuf.mtype = 1L ;
  str_len = strlen(send_str) ;
  strncpy(msgqbuf.buf, send_str, str_len) ;
  msgqbuf.buf[str_len] = '\0' ;
  istatus = msgsnd(imsgqid_a, (struct msgbuf *)&msgqbuf, str_len+1, 0) ;

  if (istatus == -1)
    istatus = errno_to_message_va( errno, 1, send_str ) ;
  else 
    istatus = SS_NORMAL ;

  return istatus ;
}

MESSAGE msgQueRecv(UINT4 imsgqid_a, char **recv_str)
{
  int     str_len = 0 ;
  MESSAGE istatus ; static struct msgquebuf msgqbuf ;

  while (str_len == 0)
    str_len = msgrcv(imsgqid_a,(struct msgbuf *)&msgqbuf,
		     MAXMSGSIZE,1L,0) ;

  if (str_len < 0)
    {
      istatus = errno_to_message_va( errno, 1, "msgQueRecv()" ) ;
      istatus = str_len ;
      str_len = 0 ;
    }
  else
    istatus = SS_NORMAL ;

  msgqbuf.buf[str_len] = '\0' ;
  *recv_str = msgqbuf.buf ;  

  return istatus ;
}

/* ------------------------------
 * Remove immediately the message queue and its data structures
 * awakening all waiting reader and writer processes with an error
 * return.
 */
MESSAGE msgQueRemove(UINT4 imsgqid_a)
{
  MESSAGE istatus ;

  istatus = msgctl(imsgqid_a, IPC_RMID, (struct msqid_ds *) 0) ;
  if (istatus == -1)
    istatus = errno_to_message_va( errno, 1, "msgQueRemove()" ) ;
  else 
    istatus = SS_NORMAL ;
  return istatus ;
}

/* ------------------------------
 * Return the number of messages in a queue.
 */
MESSAGE msgQueGetData(UINT4 imsgqid_a, UINT2 *inum_msgs_on_que_a)
{
  MESSAGE istatus ;
  struct msqid_ds data_struct ;

  istatus = msgctl(imsgqid_a, IPC_STAT,  &data_struct) ;
  if (istatus == -1)
    istatus = errno_to_message_va( errno, 1, "msgQueRemove()" ) ;
  else 
    istatus = SS_NORMAL ;

  *inum_msgs_on_que_a = data_struct.msg_qnum ;
  return istatus ;
}
