libfreerdp-utils: extend plugin utils

This commit is contained in:
Marc-André Moreau 2012-02-09 19:48:52 -05:00
parent aaaafcc4b7
commit 7a6c813fcf
5 changed files with 220 additions and 76 deletions

View File

@ -221,6 +221,7 @@ static boolean audin_receive_wave_data(uint8* data, int size, void* user_data)
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback);
if (error != 0)
return false;
@ -270,7 +271,7 @@ static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, STRE
static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN * audin = (AUDIN_PLUGIN *) callback->plugin;
AUDIN_PLUGIN * audin = (AUDIN_PLUGIN*) callback->plugin;
uint32 NewFormat;
audinFormat* format;
@ -299,9 +300,7 @@ static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallb
return 0;
}
static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
uint32 cbSize,
uint8* pBuffer)
static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, uint32 cbSize, uint8* pBuffer)
{
int error;
STREAM* s;
@ -361,9 +360,7 @@ static int audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
}
static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel,
uint8* Data,
int* pbAccept,
IWTSVirtualChannel* pChannel, uint8* Data, int* pbAccept,
IWTSVirtualChannelCallback** ppCallback)
{
AUDIN_CHANNEL_CALLBACK* callback;
@ -395,6 +392,7 @@ static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
audin->listener_callback->plugin = pPlugin;
audin->listener_callback->channel_mgr = pChannelMgr;
return pChannelMgr->CreateListener(pChannelMgr, "AUDIO_INPUT", 0,
(IWTSListenerCallback*) audin->listener_callback, NULL);
}
@ -411,6 +409,7 @@ static int audin_plugin_terminated(IWTSPlugin* pPlugin)
IFCALL(audin->device->Free, audin->device);
audin->device = NULL;
}
xfree(audin->listener_callback);
xfree(audin);
@ -439,23 +438,25 @@ static boolean audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, R
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
if (strrchr(name, '.') != NULL)
entry = (PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_plugin(name, AUDIN_DEVICE_EXPORT_FUNC_NAME);
{
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_plugin(name, AUDIN_DEVICE_EXPORT_FUNC_NAME);
}
else
{
fullname = xzalloc(strlen(name) + 8);
strcpy(fullname, "audin_");
strcat(fullname, name);
entry = (PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_plugin(fullname, AUDIN_DEVICE_EXPORT_FUNC_NAME);
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_plugin(fullname, AUDIN_DEVICE_EXPORT_FUNC_NAME);
xfree(fullname);
}
if (entry == NULL)
{
return false;
}
entryPoints.plugin = pPlugin;
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
entryPoints.plugin_data = data;
if (entry(&entryPoints) != 0)
{
DEBUG_WARN("%s entry returns error.", name);
@ -471,7 +472,7 @@ static boolean audin_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* d
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
RDP_PLUGIN_DATA default_data[2] = { { 0 }, { 0 } };
if (data->data[0] && (strcmp((char*)data->data[0], "audin") == 0 || strstr((char*)data->data[0], "/audin.") != NULL) )
if (data->data[0] && (strcmp((char*)data->data[0], "audin") == 0 || strstr((char*) data->data[0], "/audin.") != NULL))
{
if (data->data[1] && strcmp((char*)data->data[1], "format") == 0)
{
@ -490,7 +491,7 @@ static boolean audin_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* d
}
else if (data->data[1] && ((char*)data->data[1])[0])
{
return audin_load_device_plugin(pPlugin, (char*)data->data[1], data);
return audin_load_device_plugin(pPlugin, (char*) data->data[1], data);
}
else
{
@ -498,7 +499,9 @@ static boolean audin_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* d
default_data[0].data[0] = "audin";
default_data[0].data[1] = "pulse";
default_data[0].data[2] = "";
ret = audin_load_device_plugin(pPlugin, "pulse", default_data);
if (!ret)
{
default_data[0].size = sizeof(RDP_PLUGIN_DATA);
@ -507,6 +510,7 @@ static boolean audin_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* d
default_data[0].data[2] = "default";
ret = audin_load_device_plugin(pPlugin, "alsa", default_data);
}
return ret;
}
}
@ -520,6 +524,7 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
AUDIN_PLUGIN* audin;
audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
if (audin == NULL)
{
audin = xnew(AUDIN_PLUGIN);
@ -532,10 +537,7 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
}
if (error == 0)
{
audin_process_plugin_data((IWTSPlugin*) audin,
pEntryPoints->GetPluginData(pEntryPoints));
}
audin_process_plugin_data((IWTSPlugin*) audin, pEntryPoints->GetPluginData(pEntryPoints));
return error;
}

View File

@ -80,18 +80,15 @@ struct _DVCMAN_CHANNEL
STREAM* dvc_data;
};
static int dvcman_get_configuration(IWTSListener* pListener,
void** ppPropertyBag)
static int dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
{
*ppPropertyBag = NULL;
return 1;
}
static int dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
const char* pszChannelName,
uint32 ulFlags,
IWTSListenerCallback* pListenerCallback,
IWTSListener** ppListener)
const char* pszChannelName, uint32 ulFlags,
IWTSListenerCallback* pListenerCallback, IWTSListener** ppListener)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
DVCMAN_LISTENER* listener;
@ -118,13 +115,13 @@ static int dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
}
}
static int dvcman_push_event(IWTSVirtualChannelManager* pChannelMgr,
RDP_EVENT* pEvent)
static int dvcman_push_event(IWTSVirtualChannelManager* pChannelMgr, RDP_EVENT* pEvent)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
int error;
DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
error = drdynvc_push_event(dvcman->drdynvc, pEvent);
if (error == 0)
{
DEBUG_DVC("event_type %d pushed.", pEvent->event_type);
@ -133,13 +130,13 @@ static int dvcman_push_event(IWTSVirtualChannelManager* pChannelMgr,
{
DEBUG_WARN("event_type %d push failed.", pEvent->event_type);
}
return error;
}
static int dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
const char* name, IWTSPlugin* pPlugin)
static int dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name, IWTSPlugin* pPlugin)
{
DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->dvcman;
if (dvcman->num_plugins < MAX_PLUGINS)
{
@ -155,11 +152,10 @@ static int dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
}
}
IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
const char* name)
IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
{
DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
int i;
DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->dvcman;
for (i = 0; i < dvcman->num_plugins; i++)
{
@ -169,12 +165,13 @@ IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
return dvcman->plugins[i];
}
}
return NULL;
}
RDP_PLUGIN_DATA* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
{
return ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->plugin_data;
return ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->plugin_data;
}
IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
@ -187,7 +184,7 @@ IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
dvcman->drdynvc = plugin;
dvcman->channels = list_new();
return (IWTSVirtualChannelManager*)dvcman;
return (IWTSVirtualChannelManager*) dvcman;
}
int dvcman_load_plugin(IWTSVirtualChannelManager* pChannelMgr, RDP_PLUGIN_DATA* data)
@ -197,19 +194,19 @@ int dvcman_load_plugin(IWTSVirtualChannelManager* pChannelMgr, RDP_PLUGIN_DATA*
while (data && data->size > 0)
{
pDVCPluginEntry = freerdp_load_plugin((char*)data->data[0], "DVCPluginEntry");
pDVCPluginEntry = freerdp_load_plugin((char*) data->data[0], "DVCPluginEntry");
if (pDVCPluginEntry != NULL)
{
entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
entryPoints.iface.GetPlugin = dvcman_get_plugin;
entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
entryPoints.dvcman = (DVCMAN*)pChannelMgr;
entryPoints.dvcman = (DVCMAN*) pChannelMgr;
entryPoints.plugin_data = data;
pDVCPluginEntry((IDRDYNVC_ENTRY_POINTS*)&entryPoints);
pDVCPluginEntry((IDRDYNVC_ENTRY_POINTS*) &entryPoints);
}
data = (RDP_PLUGIN_DATA*)(((void*)data) + data->size);
data = (RDP_PLUGIN_DATA*)(((void*) data) + data->size);
}
return 0;
@ -219,69 +216,75 @@ static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
{
if (channel->channel_callback)
channel->channel_callback->OnClose(channel->channel_callback);
xfree(channel);
}
void dvcman_free(IWTSVirtualChannelManager* pChannelMgr)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
int i;
IWTSPlugin* pPlugin;
DVCMAN_LISTENER* listener;
DVCMAN_CHANNEL* channel;
DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
while ((channel = (DVCMAN_CHANNEL*)list_dequeue(dvcman->channels)) != NULL)
while ((channel = (DVCMAN_CHANNEL*) list_dequeue(dvcman->channels)) != NULL)
dvcman_channel_free(channel);
list_free(dvcman->channels);
for (i = 0; i < dvcman->num_listeners; i++)
{
listener = (DVCMAN_LISTENER*)dvcman->listeners[i];
listener = (DVCMAN_LISTENER*) dvcman->listeners[i];
xfree(listener->channel_name);
xfree(listener);
}
for (i = 0; i < dvcman->num_plugins; i++)
{
pPlugin = dvcman->plugins[i];
if (pPlugin->Terminated)
pPlugin->Terminated(pPlugin);
}
xfree(dvcman);
}
int dvcman_init(IWTSVirtualChannelManager* pChannelMgr)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
int i;
IWTSPlugin* pPlugin;
DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
for (i = 0; i < dvcman->num_plugins; i++)
{
pPlugin = dvcman->plugins[i];
if (pPlugin->Initialize)
pPlugin->Initialize(pPlugin, pChannelMgr);
}
return 0;
}
static int dvcman_write_channel(IWTSVirtualChannel* pChannel,
uint32 cbSize,
uint8* pBuffer,
void* pReserved)
static int dvcman_write_channel(IWTSVirtualChannel* pChannel, uint32 cbSize, uint8* pBuffer, void* pReserved)
{
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*) pChannel;
return drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize);
}
static int dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
{
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*) pChannel;
DVCMAN* dvcman = channel->dvcman;
DEBUG_DVC("id=%d", channel->channel_id);
if (list_remove(dvcman->channels, channel) == NULL)
DEBUG_WARN("channel not found");
dvcman_channel_free(channel);
return 1;
@ -289,16 +292,17 @@ static int dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
int dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, const char* ChannelName)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
int i;
int bAccept;
DVCMAN_LISTENER* listener;
DVCMAN_CHANNEL* channel;
int bAccept;
IWTSVirtualChannelCallback* pCallback;
DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
for (i = 0; i < dvcman->num_listeners; i++)
{
listener = (DVCMAN_LISTENER*)dvcman->listeners[i];
if (strcmp(listener->channel_name, ChannelName) == 0)
{
channel = xnew(DVCMAN_CHANNEL);
@ -309,13 +313,15 @@ int dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 Channel
bAccept = 1;
pCallback = NULL;
if (listener->listener_callback->OnNewChannelConnection(listener->listener_callback,
(IWTSVirtualChannel*)channel, NULL, &bAccept, &pCallback) == 0 && bAccept == 1)
(IWTSVirtualChannel*) channel, NULL, &bAccept, &pCallback) == 0 && bAccept == 1)
{
DEBUG_DVC("listener %s created new channel %d",
listener->channel_name, channel->channel_id);
channel->channel_callback = pCallback;
list_add(dvcman->channels, channel);
return 0;
}
else
@ -326,21 +332,23 @@ int dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 Channel
}
}
}
return 1;
}
static DVCMAN_CHANNEL* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
LIST_ITEM* curr;
DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
for (curr = dvcman->channels->head; curr; curr = curr->next)
{
if (((DVCMAN_CHANNEL*)curr->data)->channel_id == ChannelId)
if (((DVCMAN_CHANNEL*) curr->data)->channel_id == ChannelId)
{
return (DVCMAN_CHANNEL*)curr->data;
}
}
return NULL;
}
@ -350,19 +358,23 @@ int dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelI
IWTSVirtualChannel* ichannel;
channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
if (channel == NULL)
{
DEBUG_WARN("ChannelId %d not found!", ChannelId);
return 1;
}
if (channel->dvc_data)
{
stream_free(channel->dvc_data);
channel->dvc_data = NULL;
}
DEBUG_DVC("dvcman_close_channel: channel %d closed", ChannelId);
ichannel = (IWTSVirtualChannel*)channel;
ichannel->Close(ichannel);
return 0;
}
@ -371,13 +383,16 @@ int dvcman_receive_channel_data_first(IWTSVirtualChannelManager* pChannelMgr, ui
DVCMAN_CHANNEL* channel;
channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
if (channel == NULL)
{
DEBUG_WARN("ChannelId %d not found!", ChannelId);
return 1;
}
if (channel->dvc_data)
stream_free(channel->dvc_data);
channel->dvc_data = stream_new(length);
return 0;
@ -385,10 +400,11 @@ int dvcman_receive_channel_data_first(IWTSVirtualChannelManager* pChannelMgr, ui
int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint8* data, uint32 data_size)
{
DVCMAN_CHANNEL* channel;
int error = 0;
DVCMAN_CHANNEL* channel;
channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
if (channel == NULL)
{
DEBUG_WARN("ChannelId %d not found!", ChannelId);
@ -405,7 +421,9 @@ int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, uint32 C
channel->dvc_data = NULL;
return 1;
}
stream_write(channel->dvc_data, data, data_size);
if (stream_get_length(channel->dvc_data) >= stream_get_size(channel->dvc_data))
{
error = channel->channel_callback->OnDataReceived(channel->channel_callback,
@ -416,8 +434,7 @@ int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, uint32 C
}
else
{
error = channel->channel_callback->OnDataReceived(channel->channel_callback,
data_size, data);
error = channel->channel_callback->OnDataReceived(channel->channel_callback, data_size, data);
}
return error;

View File

@ -21,7 +21,13 @@
#define __LOAD_PLUGIN_UTILS_H
#include <freerdp/api.h>
#include <freerdp/settings.h>
FREERDP_API void* freerdp_open_library(const char* file);
FREERDP_API void* freerdp_get_library_symbol(void* library, const char* name);
FREERDP_API boolean freerdp_close_library(void* library);
FREERDP_API void* freerdp_load_library_symbol(const char* file, const char* name);
FREERDP_API void* freerdp_load_plugin(const char* name, const char* entry_name);
FREERDP_API void* freerdp_load_channel_plugin(rdpSettings* settings, const char* name, const char* entry_name);
#endif /* __LOAD_PLUGIN_UTILS_H */

View File

@ -684,6 +684,7 @@ int freerdp_channels_load_plugin(rdpChannels* channels, rdpSettings* settings, c
lib = channels->libs_data + channels->num_libs_data;
lib->entry = (PVIRTUALCHANNELENTRY) freerdp_load_plugin(name, CHANNEL_EXPORT_FUNC_NAME);
//lib->entry = (PVIRTUALCHANNELENTRY) freerdp_load_channel_plugin(settings, name, CHANNEL_EXPORT_FUNC_NAME);
if (lib->entry == NULL)
{

View File

@ -44,10 +44,82 @@
#endif
void* freerdp_open_library(const char* file)
{
void* library;
library = DLOPEN(file);
if (library == NULL)
{
printf("freerdp_load_library: failed to open %s: %s\n", file, DLERROR());
return NULL;
}
return library;
}
void* freerdp_get_library_symbol(void* library, const char* name)
{
void* symbol;
symbol = DLSYM(library, name);
if (symbol == NULL)
{
printf("freerdp_get_library_symbol: failed to load %s: %s\n", name, DLERROR());
return NULL;
}
return symbol;
}
boolean freerdp_close_library(void* library)
{
int status;
status = DLCLOSE(library);
#ifdef _WIN32
if (status != 0)
#else
if (status == 0)
#endif
{
printf("freerdp_free_library: failed to close: %s\n", DLERROR());
return false;
}
return true;
}
void* freerdp_load_library_symbol(const char* file, const char* name)
{
void* library;
void* symbol;
library = DLOPEN(file);
if (library == NULL)
{
printf("freerdp_load_library_symbol: failed to open %s: %s\n", file, DLERROR());
return NULL;
}
symbol = DLSYM(library, name);
if (symbol == NULL)
{
printf("freerdp_load_library_symbol: failed to load %s: %s\n", file, DLERROR());
return NULL;
}
return symbol;
}
void* freerdp_load_plugin(const char* name, const char* entry_name)
{
char* path;
void* module;
void* entry;
char* suffixed_name;
@ -64,28 +136,74 @@ void* freerdp_load_plugin(const char* name, const char* entry_name)
path = xstrdup(suffixed_name);
}
module = DLOPEN(path);
if (module == NULL)
{
printf("freerdp_load_plugin: failed to open %s: %s\n", path, DLERROR());
xfree(suffixed_name);
xfree(path);
return NULL;
}
entry = DLSYM(module, entry_name);
if (entry == NULL)
{
printf("freerdp_load_plugin: failed to load %s: %s\n", path, DLERROR());
xfree(suffixed_name);
xfree(path);
return NULL;
}
entry = freerdp_load_library_symbol(path, entry_name);
xfree(suffixed_name);
xfree(path);
if (entry == NULL)
{
printf("freerdp_load_plugin: failed to load %s/%s\n", name, entry_name);
return NULL;
}
return entry;
}
void* freerdp_load_channel_plugin(rdpSettings* settings, const char* name, const char* entry_name)
{
char* path;
void* entry;
char* suffixed_name;
suffixed_name = freerdp_append_shared_library_suffix((char*) name);
if (!freerdp_path_contains_separator(suffixed_name))
{
/* no explicit path given, use default path */
if (!settings->development_mode)
{
path = freerdp_construct_path(PLUGIN_PATH, suffixed_name);
}
else
{
char* dot;
char* plugin_name;
char* channels_path;
char* channel_subpath;
dot = strrchr(suffixed_name, '.');
plugin_name = xmalloc((dot - suffixed_name) + 1);
strncpy(plugin_name, suffixed_name, (dot - suffixed_name));
plugin_name[(dot - suffixed_name)] = '\0';
channels_path = freerdp_construct_path(settings->development_path, "channels");
channel_subpath = freerdp_construct_path(channels_path, plugin_name);
path = freerdp_construct_path(channel_subpath, suffixed_name);
xfree(plugin_name);
xfree(channels_path);
xfree(channel_subpath);
}
}
else
{
/* explicit path given, use it instead of default path */
path = xstrdup(suffixed_name);
}
entry = freerdp_load_library_symbol(path, entry_name);
xfree(suffixed_name);
xfree(path);
if (entry == NULL)
{
printf("freerdp_load_channel_plugin: failed to load %s/%s\n", name, entry_name);
return NULL;
}
return entry;
}