/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html.  If you do not have     *
 * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define H5L_PACKAGE		/*suppress error about including H5Lpkg   */
#define H5G_PACKAGE		/*suppress error about including H5Gpkg   */

/* Interface initialization */
#define H5_INTERFACE_INIT_FUNC	H5L_init_extern_interface

#include "H5private.h"          /* Generic Functions                    */
#include "H5Lpkg.h"             /* Links                                */
#include "H5Eprivate.h"         /* Error handling                       */
#include "H5MMprivate.h"        /* Memory management                    */
#include "H5Opublic.h"         /* File objects                         */
#include "H5Ppublic.h"         /* Property lists                       */
#include "H5Gpkg.h"             /* Groups                               */

static hid_t H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group,
    void * udata, size_t UNUSED udata_size, hid_t lapl_id);
static ssize_t H5L_extern_query(const char UNUSED * link_name, void * udata,
    size_t udata_size, void * buf /*out*/, size_t buf_size);

/* Default External Link link class */
const H5L_class_t H5L_EXTERN_LINK_CLASS[1] = {{
    H5L_LINK_CLASS_T_VERS,      /* H5L_class_t version            */
    H5L_TYPE_EXTERNAL,		/* Link type id number            */
    "external_link",            /* Link name for debugging        */
    NULL,                       /* Creation callback              */
    NULL,                       /* Move callback                  */
    NULL,                       /* Copy callback                  */
    H5L_extern_traverse,        /* The actual traversal function  */
    NULL,                       /* Deletion callback              */
    H5L_extern_query            /* Query callback                 */
}};


/*--------------------------------------------------------------------------
NAME
   H5L_init_extern_interface -- Initialize interface-specific information
USAGE
    herr_t H5L_init_extern_interface()

RETURNS
    Non-negative on success/Negative on failure

DESCRIPTION
    Initializes any interface-specific data or routines.  (Just calls
    H5L_init() currently).

--------------------------------------------------------------------------*/
static herr_t
H5L_init_extern_interface(void)
{
    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5L_init_extern_interface)

    FUNC_LEAVE_NOAPI(H5L_init())
} /* H5L_init_extern_interface() */


/*-------------------------------------------------------------------------
 * Function:	H5L_extern_traverse
 *
 * Purpose:	Default traversal function for external links. This can
 *              be overridden using H5Lregister().
 *
 *              Given a filename and path packed into the link udata,
 *              attempts to open an object within an external file.
 *              If the H5L_ELINK_PREFIX_NAME property is set in the
 *              link access property list, appends that prefix to the
 *              filename being opened.
 *
 * Return:	ID of the opened object on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, July 10, 2006
 *
 *-------------------------------------------------------------------------
 */
static hid_t
H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group,
    void * udata, size_t UNUSED udata_size, hid_t lapl_id)
{
    hid_t         fid;
    char         *file_name;
    char         *obj_name;
    ssize_t       prefix_len;           /* External link prefix length */
    size_t        fname_len;
    hbool_t       fname_alloc = FALSE;
    unsigned      intent;
    hid_t         fapl_id;
    hid_t         ret_value = -1;

    file_name = (char *) udata;
    fname_len = HDstrlen(file_name);
    obj_name = ((char *) udata) + fname_len + 1;

    /* See if the external link prefix property is set */
    if((prefix_len = H5Pget_elink_prefix(lapl_id, NULL, (size_t)0)) < 0)
        goto error;

    /* If so, prepend it to the filename */
    if(prefix_len > 0)
    {
        /* Allocate a buffer to hold the filename plus prefix */
        file_name = H5MM_malloc(prefix_len + fname_len + 1);
        fname_alloc = TRUE;

        /* Copy the prefix into the buffer */
        if(H5Pget_elink_prefix(lapl_id, file_name, (size_t)(prefix_len + 1)) < 0)
            goto error;

        /* Add the external link's filename to the prefix supplied */
        HDstrcat(file_name, udata);
    }

    /* Whatever access properties and intent the user used on the old file,
     * use the same ones to open the new file.  If this is a bad default,
     * users can override this callback using H5Lregister.
     */
    if((fid = H5Iget_file_id(cur_group)) < 0)
        goto error;
    if(H5Fget_intent(fid, &intent) < 0)
        goto error;
    if((fapl_id = H5Fget_access_plist(fid)) < 0)
        goto error;
    if(H5Fclose(fid) < 0)
        goto error;

    if((fid = H5Fopen(file_name, intent, fapl_id)) < 0)
        goto error;

    ret_value = H5Oopen(fid, obj_name, lapl_id); /* If this fails, our return value will be negative. */
    if(H5Pclose(fapl_id) < 0)
        goto error;
    if(H5Fclose(fid) < 0)
        goto error;

    /* Free file_name if it's been allocated */
    if(fname_alloc)
        H5MM_xfree(file_name);

    return ret_value;

error:
    /* Free file_name if it's been allocated */
    if(fname_alloc)
        H5MM_xfree(file_name);

    return -1;
}


/*-------------------------------------------------------------------------
 * Function:	H5L_extern_query
 *
 * Purpose:	Default query function for external links. This can
 *              be overridden using H5Lregister().
 *
 *              Returns the size of the link's user data. If a buffer of
 *              is provided, copies at most buf_size bytes of the udata
 *              into it.
 *
 * Return:	Size of buffer on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, July 10, 2006
 *
 *-------------------------------------------------------------------------
 */
static ssize_t
H5L_extern_query(const char UNUSED * link_name, void * udata,
    size_t udata_size, void * buf /*out*/, size_t buf_size)
{
    /* If the buffer is NULL, skip writing anything in it and just return
     * the size needed */
    if(buf) {
        if(udata_size < buf_size)
            buf_size = udata_size;

        /* Copy the udata verbatim up to buf_size*/
        HDmemcpy(buf, udata, buf_size);
    }

    return udata_size;
}


/*-------------------------------------------------------------------------
 * Function:	H5Lcreate_external
 *
 * Purpose:	Creates an external link from LINK_NAME to OBJ_NAME.
 *
 *              External links are links to objects in other HDF5 files.  They
 *              are allowed to "dangle" like soft links internal to a file.
 *              FILE_NAME is the name of the file that OBJ_NAME is is contained
 *              within.  If OBJ_NAME is given as a relative path name, the
 *              path will be relative to the root group of FILE_NAME.
 *		LINK_NAME is interpreted relative to LINK_LOC_ID, which is
 *              either a file ID or a group ID.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *              Wednesday, May 18, 2005
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_external(const char *file_name, const char *obj_name,
        hid_t link_loc_id, const char *link_name, hid_t lcpl_id, hid_t lapl_id)
{
    H5G_loc_t	link_loc;
    char       *temp_name = NULL;
    size_t      buf_size;
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5Lcreate_external, FAIL)
    H5TRACE6("e","ssisii",file_name,obj_name,link_loc_id,link_name,lcpl_id,
             lapl_id);

    /* Check arguments */
    if(!file_name || !*file_name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no file name specified")
    if(!obj_name || !*obj_name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no object name specified")
    if(H5G_loc(link_loc_id, &link_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!link_name || !*link_name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no link name specified")

    /* Combine the filename and link name into a single buffer to give to the UD link */
    buf_size = HDstrlen(file_name) + HDstrlen(obj_name) + 2;
    if(NULL == (temp_name = H5MM_malloc(buf_size)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate udata buffer")
    HDstrcpy(temp_name, file_name);
    HDstrcpy(temp_name + (HDstrlen(file_name) + 1), obj_name);

    /* Create an external link */
    if(H5L_create_ud(&link_loc, link_name, temp_name, buf_size, H5L_TYPE_EXTERNAL, lcpl_id, lapl_id, H5AC_dxpl_id) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create link")

done:
    if(temp_name != NULL)
        H5MM_free(temp_name);
    FUNC_LEAVE_API(ret_value);
} /* end H5Lcreate_external() */


/*-------------------------------------------------------------------------
 * Function: H5L_register_external
 *
 * Purpose: Registers default "External Link" link class.
 *              Use during library initialization or to restore the default
 *              after users change it.
 *
 * Return: Non-negative on success/ negative on failure
 *
 * Programmer:  James Laird
 *              Monday, July 17, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5L_register_external(void)
{
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5L_register_external, FAIL)

    if(H5L_register(H5L_EXTERN_LINK_CLASS) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_NOTREGISTERED, FAIL, "unable to register external link class")

done:
    FUNC_LEAVE_NOAPI(ret_value)
}


/*-------------------------------------------------------------------------
 * Function: H5Lunpack_elink_val
 *
 * Purpose: Given a buffer holding the "link value" from an external link,
 *              gets pointers to the filename and object path within the
 *              link value buffer.
 *
 *              External link linkvalues are two NULL-terminated strings
 *              one after the other.
 *
 *              FILENAME and OBJ_PATH will be set to pointers within
 *              ext_linkval unless they are NULL.
 *
 *              Using this function on strings that aren't external link
 *              udata buffers can result in segmentation faults.
 *
 * Return: Non-negative on success/ Negative on failure
 *
 * Programmer:  James Laird
 *              Monday, July 17, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lunpack_elink_val(char *ext_linkval, size_t link_size, char **filename,
                    char **obj_path)
{
    size_t      len;                /* Length of the filename in the linkval*/
    herr_t      ret_value=SUCCEED;  /* Return value */

    FUNC_ENTER_API(H5Lunpack_elink_val, FAIL)
    H5TRACE4("e","sz*s*s",ext_linkval,link_size,filename,obj_path);

    if(ext_linkval == NULL )
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not an external link linkval buffer")
    if(link_size <= 1)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid external link buffer")

    /* Try to do some error checking.  If the last character in the linkval
     * (the last character of obj_path) isn't NULL, then something's wrong.
     */
    if(ext_linkval[link_size-1] != '\0')
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "linkval buffer is not NULL-terminated")

    /* We're now guaranteed that HDstrlen won't segfault, since the buffer has
     * at least one NULL in it.
     */
    len = HDstrlen(ext_linkval);

    /* If the first NULL we found was at the very end of the buffer, then
     * this external link value has no object name and is invalid.
     */
    if(len + 1>= link_size)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "linkval buffer doesn't contain an object path")

    /* If we got here then the buffer contains (at least) two strings packed
     * in the correct way.  Assume it's correct and return pointers to the
     * filename and object path.
     */
    if(filename != NULL)
        *filename = ext_linkval;

    if(obj_path != NULL)
        *obj_path = ext_linkval + len + 1;  /* Add one for NULL terminator */

done:
    FUNC_LEAVE_API(ret_value)
}