504 lines
11 KiB
C
504 lines
11 KiB
C
/*
|
|
* utils.c - Utility functions
|
|
*
|
|
* nspluginwrapper (C) 2005-2009 Gwenole Beauchesne
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#define _GNU_SOURCE 1
|
|
#include "sysdeps.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <glib.h> /* <glib/ghash.h> */
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <sys/resource.h>
|
|
|
|
#include "utils.h"
|
|
#include "rpc.h"
|
|
|
|
#ifndef TEST_UTILS
|
|
#define XP_UNIX 1
|
|
#define MOZ_X11 1
|
|
#include <npapi.h>
|
|
#endif
|
|
|
|
#define DEBUG 1
|
|
#include "debug.h"
|
|
|
|
|
|
/* ====================================================================== */
|
|
/* === Hashes === */
|
|
/* ====================================================================== */
|
|
|
|
static GHashTable *g_ids;
|
|
|
|
static inline void *id_key(uint32_t id)
|
|
{
|
|
return (void *)(uintptr_t)id;
|
|
}
|
|
|
|
bool id_init(void)
|
|
{
|
|
if (g_ids == NULL)
|
|
g_ids = g_hash_table_new(NULL, NULL);
|
|
return g_ids != NULL;
|
|
}
|
|
|
|
void id_kill(void)
|
|
{
|
|
if (g_ids) {
|
|
g_hash_table_destroy(g_ids);
|
|
g_ids = NULL;
|
|
}
|
|
}
|
|
|
|
void id_link(int id, void *ptr)
|
|
{
|
|
g_hash_table_insert(g_ids, id_key(id), ptr);
|
|
}
|
|
|
|
int id_create(void *ptr)
|
|
{
|
|
static int id = 0;
|
|
id_link(++id, ptr);
|
|
return id;
|
|
}
|
|
|
|
bool id_remove(int id)
|
|
{
|
|
return g_hash_table_remove(g_ids, id_key(id));
|
|
}
|
|
|
|
void *id_lookup(int id)
|
|
{
|
|
return g_hash_table_lookup(g_ids, id_key(id));
|
|
}
|
|
|
|
static gboolean id_match_value(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
if (value == *(gpointer *)user_data) {
|
|
*(int *)user_data = (uintptr_t)key;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int id_lookup_value(void *ptr)
|
|
{
|
|
return g_hash_table_find(g_ids, id_match_value, &ptr) ? (uintptr_t)ptr : -1;
|
|
}
|
|
|
|
|
|
/* ====================================================================== */
|
|
/* === String expansions === */
|
|
/* ====================================================================== */
|
|
|
|
#ifndef TEST_UTILS
|
|
const char *string_of_NPError(int error)
|
|
{
|
|
const char *str;
|
|
|
|
switch ((NPError)error) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NPERR_NO_ERROR);
|
|
_(NPERR_GENERIC_ERROR);
|
|
_(NPERR_INVALID_INSTANCE_ERROR);
|
|
_(NPERR_INVALID_FUNCTABLE_ERROR);
|
|
_(NPERR_MODULE_LOAD_FAILED_ERROR);
|
|
_(NPERR_OUT_OF_MEMORY_ERROR);
|
|
_(NPERR_INVALID_PLUGIN_ERROR);
|
|
_(NPERR_INVALID_PLUGIN_DIR_ERROR);
|
|
_(NPERR_INCOMPATIBLE_VERSION_ERROR);
|
|
_(NPERR_INVALID_PARAM);
|
|
_(NPERR_INVALID_URL);
|
|
_(NPERR_FILE_NOT_FOUND);
|
|
_(NPERR_NO_DATA);
|
|
_(NPERR_STREAM_NOT_SEEKABLE);
|
|
_(NPERR_TIME_RANGE_NOT_SUPPORTED);
|
|
_(NPERR_MALFORMED_SITE);
|
|
#undef _
|
|
default:
|
|
str = "<unknown error>";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPReason(int reason)
|
|
{
|
|
const char *str;
|
|
|
|
switch ((NPReason)reason) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NPRES_DONE);
|
|
_(NPRES_NETWORK_ERR);
|
|
_(NPRES_USER_BREAK);
|
|
#undef _
|
|
default:
|
|
str = "<unknown reason>";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPStreamType(int stype)
|
|
{
|
|
const char *str;
|
|
|
|
switch (stype) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NP_NORMAL);
|
|
_(NP_SEEK);
|
|
_(NP_ASFILE);
|
|
_(NP_ASFILEONLY);
|
|
#undef _
|
|
default:
|
|
str = "<unknown stream type>";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPEvent_type(int type)
|
|
{
|
|
const char *str;
|
|
|
|
switch (type) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
#ifdef MOZ_X11
|
|
_(KeyPress);
|
|
_(KeyRelease);
|
|
_(ButtonPress);
|
|
_(ButtonRelease);
|
|
_(MotionNotify);
|
|
_(EnterNotify);
|
|
_(LeaveNotify);
|
|
_(FocusIn);
|
|
_(FocusOut);
|
|
_(KeymapNotify);
|
|
_(Expose);
|
|
_(GraphicsExpose);
|
|
_(NoExpose);
|
|
_(VisibilityNotify);
|
|
_(CreateNotify);
|
|
_(DestroyNotify);
|
|
_(UnmapNotify);
|
|
_(MapNotify);
|
|
_(MapRequest);
|
|
_(ReparentNotify);
|
|
_(ConfigureNotify);
|
|
_(ConfigureRequest);
|
|
_(GravityNotify);
|
|
_(ResizeRequest);
|
|
_(CirculateNotify);
|
|
_(CirculateRequest);
|
|
_(PropertyNotify);
|
|
_(SelectionClear);
|
|
_(SelectionRequest);
|
|
_(SelectionNotify);
|
|
_(ColormapNotify);
|
|
_(ClientMessage);
|
|
_(MappingNotify);
|
|
#endif
|
|
#undef _
|
|
default:
|
|
str = "<unknown type>";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPPVariable(int variable)
|
|
{
|
|
const char *str;
|
|
|
|
switch (variable) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NPPVpluginNameString);
|
|
_(NPPVpluginDescriptionString);
|
|
_(NPPVpluginWindowBool);
|
|
_(NPPVpluginTransparentBool);
|
|
_(NPPVjavaClass);
|
|
_(NPPVpluginWindowSize);
|
|
_(NPPVpluginTimerInterval);
|
|
_(NPPVpluginScriptableInstance);
|
|
_(NPPVpluginScriptableIID);
|
|
_(NPPVjavascriptPushCallerBool);
|
|
_(NPPVpluginKeepLibraryInMemory);
|
|
_(NPPVpluginNeedsXEmbed);
|
|
_(NPPVpluginScriptableNPObject);
|
|
_(NPPVformValue);
|
|
_(NPPVpluginUrlRequestsDisplayedBool);
|
|
_(NPPVpluginWantsAllNetworkStreams);
|
|
_(NPPVpluginNativeAccessibleAtkPlugId);
|
|
_(NPPVpluginCancelSrcStream);
|
|
_(NPPVsupportsAdvancedKeyHandling);
|
|
_(NPPVpluginUsesDOMForCursorBool);
|
|
#undef _
|
|
default:
|
|
switch (variable & 0xff) {
|
|
#define _(VAL, VAR) case VAL: str = #VAR; break
|
|
_(10, NPPVpluginScriptableInstance);
|
|
#undef _
|
|
default:
|
|
str = "<unknown variable>";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPNVariable(int variable)
|
|
{
|
|
const char *str;
|
|
|
|
switch (variable) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NPNVxDisplay);
|
|
_(NPNVxtAppContext);
|
|
_(NPNVnetscapeWindow);
|
|
_(NPNVjavascriptEnabledBool);
|
|
_(NPNVasdEnabledBool);
|
|
_(NPNVisOfflineBool);
|
|
_(NPNVserviceManager);
|
|
_(NPNVDOMElement);
|
|
_(NPNVDOMWindow);
|
|
_(NPNVToolkit);
|
|
_(NPNVSupportsXEmbedBool);
|
|
_(NPNVWindowNPObject);
|
|
_(NPNVPluginElementNPObject);
|
|
_(NPNVSupportsWindowless);
|
|
_(NPNVprivateModeBool);
|
|
_(NPNVsupportsAdvancedKeyHandling);
|
|
_(NPNVdocumentOrigin);
|
|
#undef _
|
|
default:
|
|
switch (variable & 0xff) {
|
|
#define _(VAL, VAR) case VAL: str = #VAR; break
|
|
_(10, NPNVserviceManager);
|
|
_(11, NPNVDOMElement);
|
|
_(12, NPNVDOMWindow);
|
|
_(13, NPNVToolkit);
|
|
#undef _
|
|
default:
|
|
str = "<unknown variable>";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPNURLVariable(int variable)
|
|
{
|
|
const char *str;
|
|
|
|
switch (variable) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NPNURLVCookie);
|
|
_(NPNURLVProxy);
|
|
#undef _
|
|
default:
|
|
str = "<unknown variable>";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *string_of_NPWindowType(int type)
|
|
{
|
|
const char *str;
|
|
|
|
switch (type) {
|
|
#define _(VAL) case VAL: str = #VAL; break;
|
|
_(NPWindowTypeWindow);
|
|
_(NPWindowTypeDrawable);
|
|
#undef _
|
|
default:
|
|
str = "<unknown type>";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* ====================================================================== */
|
|
/* === Misc utility functions === */
|
|
/* ====================================================================== */
|
|
|
|
void npw_perror(const char *prefix, int error)
|
|
{
|
|
if (prefix && *prefix)
|
|
npw_printf("ERROR: %s: %s\n", prefix, npw_strerror(error));
|
|
else
|
|
npw_printf("ERROR: %s\n", npw_strerror(error));
|
|
}
|
|
|
|
const char *npw_strerror(int error)
|
|
{
|
|
if (error > -1100 && error <= -1000) // RPC errors
|
|
return rpc_strerror(error);
|
|
|
|
switch (error) {
|
|
case 0: return "No error";
|
|
}
|
|
|
|
return "Unknown error";
|
|
}
|
|
|
|
/* Return 1 + max value the system can allocate to a new fd */
|
|
static int get_open_max(void)
|
|
{
|
|
int open_max = -1;
|
|
/* SCO OpenServer has an fcntl() to retrieve the highest *currently
|
|
open* file descriptor. */
|
|
#ifdef F_GETHFDO
|
|
if ((open_max = fcntl(-1, F_GETHFDO, 0)) >= 0)
|
|
return open_max + 1;
|
|
#endif
|
|
/* IEEE Std 1003.1-2001/Cor 1-2002 clarified the fact that return
|
|
value of sysconf(_SC_OPEN_MAX) may change if setrlimit() was
|
|
called to set RLIMIT_NOFILE. So, we should be on the safe side to
|
|
call getrlimit() first to get the soft limit.
|
|
|
|
Note: dgettablesize() was a possibility but (i) it's equivalent
|
|
to getrlimit(), and (ii) it is not recommended for new code. */
|
|
struct rlimit ru;
|
|
if (getrlimit(RLIMIT_NOFILE, &ru) == 0)
|
|
return ru.rlim_cur;
|
|
if ((open_max = sysconf(_SC_OPEN_MAX)) >= 0)
|
|
return open_max;
|
|
/* XXX: simply guess something reasonable. */
|
|
return 256;
|
|
}
|
|
|
|
void npw_close_all_open_files(void)
|
|
{
|
|
const int min_fd = 3;
|
|
|
|
#if defined(__linux__)
|
|
DIR *dir = opendir("/proc/self/fd");
|
|
if (dir) {
|
|
const int dfd = dirfd(dir);
|
|
struct dirent *d;
|
|
while ((d = readdir(dir)) != NULL) {
|
|
char *end;
|
|
long n = strtol(d->d_name, &end, 10);
|
|
if (*end == '\0') {
|
|
int fd = n;
|
|
if (fd >= min_fd && fd != dfd)
|
|
close(fd);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
const int open_max = get_open_max();
|
|
for (int fd = min_fd; fd < open_max; fd++)
|
|
close(fd);
|
|
}
|
|
|
|
|
|
/* ====================================================================== */
|
|
/* === Test Program === */
|
|
/* ====================================================================== */
|
|
|
|
#ifdef TEST_UTILS
|
|
int main(void)
|
|
{
|
|
char *str;
|
|
int i, id;
|
|
|
|
id_init();
|
|
|
|
#define N_CELLS_PER_SLOT 8
|
|
#define N_STRINGS ((2 * N_CELLS_PER_SLOT) + 3)
|
|
char *strings[N_STRINGS];
|
|
int ids[N_STRINGS];
|
|
|
|
for (i = 0; i < N_STRINGS; i++) {
|
|
str = malloc(10);
|
|
sprintf(str, "%d", i);
|
|
strings[i] = str;
|
|
id = id_create(str);
|
|
if (id < 0) {
|
|
fprintf(stderr, "ERROR: failed to allocate ID for '%s'\n", str);
|
|
return 1;
|
|
}
|
|
ids[i] = id;
|
|
}
|
|
|
|
// basic lookup
|
|
id = ids[N_CELLS_PER_SLOT / 2];
|
|
str = id_lookup(id);
|
|
printf("str(%d) : '%s'\n", id, str);
|
|
|
|
// basic unlink
|
|
id = ids[N_CELLS_PER_SLOT];
|
|
if (id_remove(id) < 0) {
|
|
fprintf(stderr, "ERROR: failed to unlink ID %d\n", id);
|
|
return 1;
|
|
}
|
|
ids[N_CELLS_PER_SLOT] = 0;
|
|
|
|
// remove slot 1
|
|
for (i = 0; i < N_CELLS_PER_SLOT; i++) {
|
|
id = ids[N_CELLS_PER_SLOT + i];
|
|
if (id && id_remove(id) < 0) {
|
|
fprintf(stderr, "ERROR: failed to unlink ID %d from slot 1\n", id);
|
|
return 1;
|
|
}
|
|
ids[N_CELLS_PER_SLOT + i] = 0;
|
|
}
|
|
|
|
// basic lookup after slot removal
|
|
id = ids[2 * N_CELLS_PER_SLOT];
|
|
str = id_lookup(id);
|
|
printf("str(%d) : '%s'\n", id, str);
|
|
|
|
// check slot 1 was removed and slots 0 & 2 linked together
|
|
for (i = 0; i < N_STRINGS; i++) {
|
|
id = ids[i];
|
|
if (id && id_remove(id) < 0) {
|
|
fprintf(stderr, "ERROR: failed to unlink ID %d for final cleanup\n", id);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < N_STRINGS; i++)
|
|
free(strings[i]);
|
|
|
|
id_kill();
|
|
return 0;
|
|
}
|
|
#endif
|