/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 * Copyright (c) 2026-present Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** POSIX wrapper for THREADX                                             */
/**                                                                       */
/**                                                                       */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

/* Include necessary system files.  */

#include "tx_api.h"     /* Threadx API */
#include "pthread.h"    /* Posix API */
#include "px_int.h"     /* Posix helper functions */


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    mq_open                                             PORTABLE C      */
/*                                                           6.2.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This routine establishes connection between a named message queue   */
/*    and the calling a thread                                            */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    mqName                      name of the queue to open.              */
/*    oflags                      O_RDONLY, O_WRONLY, O_RDWR, or O_CREAT, */
/*                                O_EXCEL,O_NONBLOCK.                     */
/*                                extra optional parameters.              */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*     queue_des                  If successful                           */
/*     ERROR                      If fails                                */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    posix_find_queue            find queue of given name                */
/*    posix_mq_create             create a queue                          */
/*    posix_get_queue_des         gets a queue-descriptor for Queue       */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application Code                                                    */
/*                                                                        */
/**************************************************************************/
mqd_t  mq_open(const CHAR * mqName, ULONG oflags,...)
{

POSIX_MSG_QUEUE     *posix_queue;
struct mq_des       *queue_des;
struct mq_attr      *q_attr;
mode_t               mode;
va_list              create_queue;
ULONG                len;
ULONG                temp1;

    len = strlen(mqName);
    if(len > PATH_MAX)
    {
        /* Return error.  */
        posix_errno = ENAMETOOLONG;
        posix_set_pthread_errno(ENAMETOOLONG);

        /*. Return error.  */
        return((struct mq_des *)ERROR);
    }
    switch(oflags & 0xFF00)
    {
        case O_CREAT:

        case (O_EXCL | O_CREAT):

             va_start(create_queue, oflags);
             mode   = va_arg(create_queue, mode_t);
             mode = mode;  /* just to keep the complier happy */
             q_attr = va_arg(create_queue, struct mq_attr *);
             va_end(create_queue);

             /* Check for valid messages and its size.  */
             if(!q_attr || q_attr->mq_maxmsg > MQ_MAXMSG || q_attr->mq_msgsize > MQ_MSGSIZE)
             {
                 /* return POSIX error.for invalid oflag.  */
                 posix_errno = EINVAL;
                 posix_set_pthread_errno(EINVAL);
                 /* return error.  */
                 return ((struct mq_des *)ERROR);
             }

             /* Check if name is exist. NULL if successful.  */
             if((posix_queue = posix_find_queue(mqName)) != NULL)
             {
                 if(posix_queue->unlink_flag == TX_TRUE)
                 {
                     /* return POSIX error.  */
                     posix_errno = ENOENT;
                 posix_set_pthread_errno(ENOENT);
                    /* return error.  */
                    return ((struct mq_des *)ERROR);
                 }

                 /* Set Posix error if name exist.  */
                 posix_errno = EEXIST;
                 posix_set_pthread_errno(EEXIST);
                 /* return error */
                 return((struct mq_des *)ERROR);
             }

             /* If q_attr is NULL then the default attributes of the struct
                mq_attr are used */
             if(q_attr == NULL)
             {
                 q_attr = &(posix_qattr_default);
                 temp1 = q_attr->mq_maxmsg;
                 temp1= temp1 ;         /* Just to keep complier happy */
             }

             /* Create a queue which returns posix queue if successful and
                NULL if fails.  */
             if(!(posix_queue = posix_mq_create(mqName, q_attr)))
             {

                /* posix_errno is filled up in mq_create.  */
                return((struct mq_des *)ERROR);
             }
             /* open count incremented by one.  */
             posix_queue->open_count += 1;
             break;

        case O_EXCL:
            /* Check if name is exist. NULL if successful.  */
            if(!(posix_queue = posix_find_queue(mqName)))
            {
                /* return POSIX error.  */
                posix_errno = EBADF;
                posix_set_pthread_errno(EBADF);
                /* return error.  */
                return ((struct mq_des *)ERROR);
            }

            return(OK);

        case O_RDONLY:
        case O_WRONLY:
        case O_RDWR:
        case O_NONBLOCK:
            /* Check if name is exist. NULL if successful.  */
            if((posix_queue = posix_find_queue(mqName)) != NULL)
            {
                if(posix_queue->unlink_flag == TX_TRUE)
                {
                    /* return POSIX error.  */
                    posix_errno = ENOENT;
                    posix_set_pthread_errno(ENOENT);
                    /* return error.  */
                    return ((struct mq_des *)ERROR);
                }
                /* open count incremented by one.  */
                posix_queue->open_count += 1;
            }
            break;

    default:
            /* return POSIX error.for invalid oflag.  */
            posix_errno = EINVAL;
            posix_set_pthread_errno(EINVAL);
            /* return error.  */
            return ((struct mq_des *)ERROR);
    }

    queue_des = posix_get_queue_des(posix_queue);
    /* Store the flags.  */
    queue_des->f_flag = oflags;
    return(queue_des);
}
