2015-09-30 12:03:46 +02:00
|
|
|
/********************************************************************
|
|
|
|
* OBIDMS header file *
|
|
|
|
********************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file obidmscolumn.h
|
|
|
|
* @author Eric Coissac (eric.coissac@metabarcoding.org)
|
|
|
|
* @date 23 May 2015
|
|
|
|
* @brief Header file for the OBIDMS functions and structures.
|
2015-05-23 16:29:06 +03:00
|
|
|
*/
|
|
|
|
|
2015-09-30 12:03:46 +02:00
|
|
|
|
2015-05-23 16:29:06 +03:00
|
|
|
#ifndef OBIDMS_H_
|
|
|
|
#define OBIDMS_H_
|
|
|
|
|
2015-09-30 12:03:46 +02:00
|
|
|
|
2015-05-23 16:29:06 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <dirent.h>
|
2015-05-26 10:38:56 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2015-11-18 15:35:09 +01:00
|
|
|
#include <stdbool.h>
|
2015-05-23 16:29:06 +03:00
|
|
|
|
2015-06-17 16:51:16 +02:00
|
|
|
#include "obierrno.h"
|
2016-04-22 11:28:09 +02:00
|
|
|
#include "obitypes.h"
|
2015-05-23 16:29:06 +03:00
|
|
|
|
2015-09-30 12:03:46 +02:00
|
|
|
|
2016-09-22 18:05:07 +02:00
|
|
|
#define OBIDMS_MAX_NAME (247) /**< The maximum length of an OBIDMS name.
|
2016-04-12 14:53:33 +02:00
|
|
|
*/
|
|
|
|
#define INDEXER_DIR_NAME "OBIBLOB_INDEXERS" /**< The name of the Obiblob indexer directory.
|
|
|
|
*/
|
2016-06-30 11:41:30 +02:00
|
|
|
#define VIEW_DIR_NAME "VIEWS" /**< The name of the view directory.
|
|
|
|
*/
|
2016-04-12 14:53:33 +02:00
|
|
|
#define TAXONOMY_DIR_NAME "TAXONOMY" /**< The name of the taxonomy directory.
|
|
|
|
*/
|
2016-09-22 18:05:07 +02:00
|
|
|
#define MAX_NB_OPENED_COLUMNS (1000) /**< The maximum number of columns open at the same time.
|
2016-04-12 14:53:33 +02:00
|
|
|
*/
|
2016-09-22 18:05:07 +02:00
|
|
|
#define MAX_NB_OPENED_INDEXERS (1000) /**< The maximum number of indexers open at the same time.
|
2016-04-12 14:53:33 +02:00
|
|
|
*/
|
2016-09-22 18:05:07 +02:00
|
|
|
#define MAX_PATH_LEN (1024) /**< Maximum length for the character string defining a
|
2016-04-15 10:49:12 +02:00
|
|
|
* file or directory path.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
struct OBIDMS_column; /**< Declarations to avoid circular dependencies. */
|
|
|
|
typedef struct OBIDMS_column* OBIDMS_column_p; /**< Declarations to avoid circular dependencies. */
|
2015-12-02 17:32:07 +01:00
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Structure listing the columns opened in a DMS, identified by their name and version number.
|
|
|
|
*/
|
|
|
|
typedef struct Opened_columns_list {
|
|
|
|
int nb_opened_columns; /**< Number of opened columns.
|
|
|
|
*/
|
|
|
|
OBIDMS_column_p columns[MAX_NB_OPENED_COLUMNS]; /**< Array of pointers on the opened columns.
|
|
|
|
*/
|
2015-12-02 17:32:07 +01:00
|
|
|
} Opened_columns_list_t, *Opened_columns_list_p;
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
// TODO Need to find a way to not refer to AVLs specifically
|
|
|
|
struct OBIDMS_avl_group; /**< Declarations to avoid circular dependencies. */
|
|
|
|
typedef struct OBIDMS_avl_group* OBIDMS_avl_group_p; /**< Declarations to avoid circular dependencies. */
|
|
|
|
typedef OBIDMS_avl_group_p Obi_indexer_p; /**< Declarations to avoid circular dependencies. */
|
2015-12-02 17:32:07 +01:00
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Structure listing the indexers opened in a DMS, identified by their name.
|
|
|
|
*/
|
2016-04-12 14:53:33 +02:00
|
|
|
typedef struct Opened_indexers_list {
|
2016-04-22 11:28:09 +02:00
|
|
|
int nb_opened_indexers; /**< Number of opened indexers.
|
|
|
|
*/
|
|
|
|
Obi_indexer_p indexers[MAX_NB_OPENED_INDEXERS]; /**< Array of pointers on the opened indexers.
|
|
|
|
*/
|
2016-04-12 14:53:33 +02:00
|
|
|
} Opened_indexers_list_t, *Opened_indexers_list_p;
|
2015-05-23 16:29:06 +03:00
|
|
|
|
2015-09-30 12:03:46 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief A structure describing an OBIDMS instance
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
|
|
|
* A pointer to this structure is returned on creation
|
|
|
|
* and opening of an OBITools Data Management System (DMS)
|
|
|
|
*/
|
|
|
|
typedef struct OBIDMS {
|
2016-04-29 17:46:36 +02:00
|
|
|
char dms_name[OBIDMS_MAX_NAME+1]; /** The name of the DMS.
|
|
|
|
*/
|
2015-12-02 17:32:07 +01:00
|
|
|
char directory_name[OBIDMS_MAX_NAME+1]; /**< The name of the directory
|
|
|
|
* containing the DMS.
|
|
|
|
*/
|
2016-04-29 17:46:36 +02:00
|
|
|
char directory_path[MAX_PATH_LEN]; /**< The absolute path of the directory
|
|
|
|
* containing the DMS.
|
|
|
|
*/
|
2015-12-02 17:32:07 +01:00
|
|
|
DIR* directory; /**< A directory entry usable to
|
|
|
|
* refer and scan the database directory.
|
|
|
|
*/
|
|
|
|
int dir_fd; /**< The file descriptor of the directory entry
|
|
|
|
* usable to refer and scan the database directory.
|
|
|
|
*/
|
2016-04-22 11:28:09 +02:00
|
|
|
DIR* indexer_directory; /**< A directory entry usable to
|
2016-04-12 14:53:33 +02:00
|
|
|
* refer and scan the indexer directory.
|
2015-12-02 17:32:07 +01:00
|
|
|
*/
|
2016-04-22 11:28:09 +02:00
|
|
|
int indexer_dir_fd; /**< The file descriptor of the directory entry
|
2016-04-12 14:53:33 +02:00
|
|
|
* usable to refer and scan the indexer directory.
|
2015-12-02 17:32:07 +01:00
|
|
|
*/
|
2016-06-30 11:41:30 +02:00
|
|
|
DIR* view_directory; /**< A directory entry usable to
|
|
|
|
* refer and scan the view directory.
|
|
|
|
*/
|
|
|
|
int view_dir_fd; /**< The file descriptor of the directory entry
|
|
|
|
* usable to refer and scan the view directory.
|
|
|
|
*/
|
2016-10-14 17:03:10 +02:00
|
|
|
DIR* tax_directory; /**< A directory entry usable to
|
|
|
|
* refer and scan the taxonomy directory.
|
|
|
|
*/
|
|
|
|
int tax_dir_fd; /**< The file descriptor of the directory entry
|
|
|
|
* usable to refer and scan the taxonomy directory.
|
|
|
|
*/
|
2015-12-02 17:32:07 +01:00
|
|
|
bool little_endian; /**< Endianness of the database.
|
|
|
|
*/
|
|
|
|
Opened_columns_list_p opened_columns; /**< List of opened columns.
|
|
|
|
*/
|
2016-04-12 14:53:33 +02:00
|
|
|
Opened_indexers_list_p opened_indexers; /**< List of opened indexers.
|
2015-12-02 17:32:07 +01:00
|
|
|
*/
|
2015-05-23 16:29:06 +03:00
|
|
|
} OBIDMS_t, *OBIDMS_p;
|
|
|
|
|
|
|
|
|
2015-09-30 12:03:46 +02:00
|
|
|
/**
|
|
|
|
* @brief Checks if an OBIDMS exists.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
2016-04-29 17:46:36 +02:00
|
|
|
* @param dms_path A pointer to a C string containing the path to the database.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @returns An integer value indicating the status of the database.
|
2015-09-30 12:03:46 +02:00
|
|
|
* @retval 1 if the database exists.
|
|
|
|
* @retval 0 if the database does not exist.
|
|
|
|
* @retval -1 if an error occurred.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
|
|
|
* @since May 2015
|
|
|
|
* @author Eric Coissac (eric.coissac@metabarcoding.org)
|
|
|
|
*/
|
2016-04-29 17:46:36 +02:00
|
|
|
int obi_dms_exists(const char* dms_path);
|
2015-05-23 16:29:06 +03:00
|
|
|
|
2015-06-10 15:19:02 +02:00
|
|
|
|
2015-05-23 16:29:06 +03:00
|
|
|
/**
|
2015-06-10 15:19:02 +02:00
|
|
|
* @brief Creates a new OBITools Data Management instance (OBIDMS).
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2015-06-10 15:19:02 +02:00
|
|
|
* A `DMS` is implemented as a directory. This function checks
|
|
|
|
* if a directory with this name does not already exist
|
|
|
|
* before creating the new database.
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2016-04-12 14:53:33 +02:00
|
|
|
* A directory to store Obiblob indexers is also created.
|
2015-11-06 17:55:15 +01:00
|
|
|
*
|
2016-04-29 17:46:36 +02:00
|
|
|
* @param dms_path A pointer to a C string containing the path to the database.
|
2015-09-30 12:03:46 +02:00
|
|
|
* The actual directory name used to store the DMS will be
|
|
|
|
* `<dms_name>.obidms`.
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2015-09-30 12:03:46 +02:00
|
|
|
* @returns A pointer to an OBIDMS structure describing the newly created DMS.
|
|
|
|
* @retval NULL if an error occurred.
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
|
|
|
* @see obi_close_dms()
|
|
|
|
* @since May 2015
|
|
|
|
* @author Eric Coissac (eric.coissac@metabarcoding.org)
|
|
|
|
*/
|
2015-09-30 12:03:46 +02:00
|
|
|
OBIDMS_p obi_create_dms(const char* dms_name);
|
2015-05-23 16:29:06 +03:00
|
|
|
|
2015-06-10 15:19:02 +02:00
|
|
|
|
2015-05-23 16:29:06 +03:00
|
|
|
/**
|
2015-06-10 15:19:02 +02:00
|
|
|
* @brief Opens an existing OBITools Data Management instance (OBIDMS).
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2016-04-29 17:46:36 +02:00
|
|
|
* @param dms_path A pointer to a C string containing the path to the database.
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2015-09-30 12:03:46 +02:00
|
|
|
* @returns A pointer to an OBIDMS structure describing the opened DMS.
|
|
|
|
* @retval NULL if an error occurred.
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
|
|
|
* @see obi_close_dms()
|
|
|
|
* @since May 2015
|
|
|
|
* @author Eric Coissac (eric.coissac@metabarcoding.org)
|
|
|
|
*/
|
2016-04-29 17:46:36 +02:00
|
|
|
OBIDMS_p obi_open_dms(const char* dms_path);
|
2015-05-23 16:29:06 +03:00
|
|
|
|
2015-06-10 15:19:02 +02:00
|
|
|
|
2017-07-05 15:38:22 +02:00
|
|
|
/**
|
|
|
|
* @brief Opens an existing OBITools Data Management instance (OBIDMS).
|
|
|
|
*
|
|
|
|
* @warning No error is printed or saved if the DMS does not exist. For it to be the case, use obi_open_dms().
|
|
|
|
*
|
|
|
|
* @param dms_path A pointer to a C string containing the path to the database.
|
|
|
|
*
|
|
|
|
* @returns A pointer to an OBIDMS structure describing the opened DMS.
|
|
|
|
* @retval NULL if the DMS does not exist or if an error occurred.
|
|
|
|
*
|
|
|
|
* @see obi_open_dms()
|
|
|
|
* @see obi_close_dms()
|
|
|
|
* @since May 2017
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
|
|
|
OBIDMS_p obi_test_open_dms(const char* dms_name);
|
|
|
|
|
|
|
|
|
2015-05-26 21:36:55 +02:00
|
|
|
/**
|
2015-09-30 12:03:46 +02:00
|
|
|
* @brief Creates or opens a new OBIDMS instance.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
2015-06-10 15:19:02 +02:00
|
|
|
* If the database already exists, this function opens it, otherwise it
|
|
|
|
* creates a new database.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
2016-04-29 17:46:36 +02:00
|
|
|
* @param dms_path A pointer to a C string containing the path to the database.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
2015-09-30 12:03:46 +02:00
|
|
|
* @returns A pointer to an OBIDMS structure describing the OBIDMS.
|
|
|
|
* @retval NULL if an error occurred.
|
2015-05-26 21:36:55 +02:00
|
|
|
*
|
|
|
|
* @see obi_close_dms()
|
|
|
|
* @since May 2015
|
|
|
|
* @author Eric Coissac (eric.coissac@metabarcoding.org)
|
|
|
|
*/
|
2016-04-29 17:46:36 +02:00
|
|
|
OBIDMS_p obi_dms(const char* dms_path);
|
2015-05-26 21:36:55 +02:00
|
|
|
|
2015-06-10 15:19:02 +02:00
|
|
|
|
2015-05-23 16:29:06 +03:00
|
|
|
/**
|
2015-06-10 15:19:02 +02:00
|
|
|
* @brief Closes an opened OBITools Data Management instance (OBIDMS).
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2015-09-30 12:03:46 +02:00
|
|
|
* @param dms A pointer as returned by obi_create_dms() or obi_open_dms().
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
2015-09-30 12:03:46 +02:00
|
|
|
* @returns An integer value indicating the success of the operation. Even on
|
|
|
|
* error, the `OBIDMS` structure is freed.
|
|
|
|
* @retval 0 on success.
|
|
|
|
* @retval -1 if an error occurred?-.
|
2015-05-23 16:29:06 +03:00
|
|
|
*
|
|
|
|
* @see obi_create_dms()
|
|
|
|
* @see obi_open_dms()
|
|
|
|
* @since May 2015
|
|
|
|
* @author Eric Coissac (eric.coissac@metabarcoding.org)
|
|
|
|
*/
|
|
|
|
int obi_close_dms(OBIDMS_p dms);
|
|
|
|
|
2015-06-10 15:19:02 +02:00
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Returns a column identified by its name and its version number from the list of opened columns.
|
|
|
|
*
|
|
|
|
* @param dms The OBIDMS.
|
|
|
|
* @param column_name The column name that should be looked for.
|
|
|
|
* @param version The version number of the column that should be looked for.
|
|
|
|
*
|
|
|
|
* @returns A pointer on the column if it was found in the list of opened columns.
|
|
|
|
* @retval NULL if the column was not found in the list of opened columns.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 10:49:12 +02:00
|
|
|
OBIDMS_column_p obi_dms_get_column_from_list(OBIDMS_p dms, const char* column_name, obiversion_t version);
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Adds a column identified by its name and its version number in the list of opened columns.
|
|
|
|
*
|
2016-04-29 16:06:01 +02:00
|
|
|
* @warning obi_dms_get_column_from_list() should be used first to check if the column isn't already listed.
|
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @param dms The OBIDMS.
|
|
|
|
* @param column A pointer on the column that should be added in the list of opened columns.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 10:49:12 +02:00
|
|
|
void obi_dms_list_column(OBIDMS_p dms, OBIDMS_column_p column);
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Removes a column identified by its name and its version number from the list of opened columns.
|
|
|
|
*
|
|
|
|
* @param dms The OBIDMS.
|
|
|
|
* @param column A pointer on the column that should be removed from the list of opened columns.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 10:49:12 +02:00
|
|
|
int obi_dms_unlist_column(OBIDMS_p dms, OBIDMS_column_p column);
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Returns an indexer identified by its name from the list of opened indexers.
|
|
|
|
*
|
|
|
|
* @param dms The OBIDMS.
|
|
|
|
* @param indexer_name The indexer name that should be looked for.
|
|
|
|
*
|
|
|
|
* @returns A pointer on the indexer if it was found in the list of opened indexers.
|
|
|
|
* @retval NULL if the indexer was not found in the list of opened indexers.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 10:49:12 +02:00
|
|
|
Obi_indexer_p obi_dms_get_indexer_from_list(OBIDMS_p dms, const char* indexer_name);
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Adds an indexer identified by its name in the list of opened indexers.
|
|
|
|
*
|
2016-04-29 16:06:01 +02:00
|
|
|
* @warning obi_dms_get_indexer_from_list() should be used first to check if the indexer isn't already listed.
|
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @param dms The OBIDMS.
|
|
|
|
* @param indexer A pointer on the indexer that should be added in the list of opened indexers.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 10:49:12 +02:00
|
|
|
void obi_dms_list_indexer(OBIDMS_p dms, Obi_indexer_p indexer);
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Removes an indexer identified by its name from the list of opened indexers.
|
|
|
|
*
|
|
|
|
* @param dms The OBIDMS.
|
|
|
|
* @param column A pointer on the indexer that should be removed from the list of opened indexers.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 10:49:12 +02:00
|
|
|
int obi_dms_unlist_indexer(OBIDMS_p dms, Obi_indexer_p indexer);
|
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
2016-04-29 17:46:36 +02:00
|
|
|
* @brief Gets the full path to the DMS.
|
|
|
|
*
|
|
|
|
* @warning The returned pointer has to be freed by the caller.
|
|
|
|
*
|
|
|
|
* @param dms The DMS.
|
|
|
|
*
|
|
|
|
* @returns A pointer to the full path.
|
|
|
|
* @retval NULL if an error occurred.
|
|
|
|
*
|
|
|
|
* @since April 2016
|
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
2016-04-22 11:28:09 +02:00
|
|
|
*/
|
2016-04-29 17:46:36 +02:00
|
|
|
char* obi_dms_get_dms_path(OBIDMS_p dms);
|
2016-04-15 11:11:13 +02:00
|
|
|
|
|
|
|
|
2016-04-22 11:28:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Gets the full path of a file or a directory from its
|
|
|
|
* path relative to the DMS.
|
2016-04-15 10:49:12 +02:00
|
|
|
*
|
|
|
|
* @warning The returned pointer has to be freed by the caller.
|
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @param dms The DMS to which path_name is relative.
|
|
|
|
* @param path_name The path name for the file or directory, relative to the DMS.
|
2016-04-15 10:49:12 +02:00
|
|
|
*
|
|
|
|
* @returns A pointer to the full path.
|
|
|
|
* @retval NULL if an error occurred.
|
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @since April 2016
|
2016-04-15 10:49:12 +02:00
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
2016-04-15 11:11:13 +02:00
|
|
|
char* obi_dms_get_full_path(OBIDMS_p dms, const char* path_name);
|
2016-04-15 10:49:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-04-22 11:28:09 +02:00
|
|
|
* @brief Opens a directory relative to the DMS.
|
2016-04-15 10:49:12 +02:00
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @param dms The DMS to which path_name is relative.
|
|
|
|
* @param path_name The path name for the directory to be opened, relative to the DMS.
|
2016-04-15 10:49:12 +02:00
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @returns The directory stream of the opened directory.
|
2016-04-15 10:49:12 +02:00
|
|
|
* @retval NULL if an error occurred.
|
|
|
|
*
|
2016-04-22 11:28:09 +02:00
|
|
|
* @since April 2016
|
2016-04-15 10:49:12 +02:00
|
|
|
* @author Celine Mercier (celine.mercier@metabarcoding.org)
|
|
|
|
*/
|
|
|
|
DIR* opendir_in_dms(OBIDMS_p dms, const char* path_name);
|
|
|
|
|
|
|
|
|
2015-05-23 16:29:06 +03:00
|
|
|
#endif /* OBIDMS_H_ */
|