diff options
Diffstat (limited to 'backend/dll.c')
-rw-r--r-- | backend/dll.c | 1305 |
1 files changed, 1305 insertions, 0 deletions
diff --git a/backend/dll.c b/backend/dll.c new file mode 100644 index 0000000..619ee55 --- /dev/null +++ b/backend/dll.c @@ -0,0 +1,1305 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 1996, 1997 David Mosberger-Tang + This file is part of the SANE package. + + 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., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. + + This file implements a dynamic linking based SANE meta backend. It + allows managing an arbitrary number of SANE backends by using + dynamic linking to load backends on demand. */ + +/* Please increase version number with every change + (don't forget to update dll.desc) */ +#define DLL_VERSION "1.0.13" + +#ifdef _AIX +# include "lalloca.h" /* MUST come first for AIX! */ +#endif + +#ifdef __BEOS__ +#include <kernel/OS.h> +#include <storage/FindDirectory.h> +#include <kernel/image.h> +#include <posix/dirent.h> +#endif + +#include "../include/sane/config.h" +#include "lalloca.h" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(HAVE_DLOPEN) && defined(HAVE_DLFCN_H) +# include <dlfcn.h> + + /* Older versions of dlopen() don't define RTLD_NOW and RTLD_LAZY. + They all seem to use a mode of 1 to indicate RTLD_NOW and some do + not support RTLD_LAZY at all. Hence, unless defined, we define + both macros as 1 to play it safe. */ +# ifndef RTLD_NOW +# define RTLD_NOW 1 +# endif +# ifndef RTLD_LAZY +# define RTLD_LAZY 1 +# endif +# define HAVE_DLL +#endif + +/* HP/UX DLL support */ +#if defined (HAVE_SHL_LOAD) && defined(HAVE_DL_H) +# include <dl.h> +# define HAVE_DLL +#endif + +/* Mac OS X/Darwin support */ +#if defined (HAVE_NSLINKMODULE) && defined(HAVE_MACH_O_DYLD_H) +# include <mach-o/dyld.h> +# define HAVE_DLL +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" + +#define BACKEND_NAME dll +#include "../include/sane/sanei_backend.h" + +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +#if defined(_WIN32) || defined(HAVE_OS2_H) +# define DIR_SEP ";" +#else +# define DIR_SEP ":" +#endif + + +#include "../include/sane/sanei_config.h" +#define DLL_CONFIG_FILE "dll.conf" +#define DLL_ALIASES_FILE "dll.aliases" + +enum SANE_Ops +{ + OP_INIT = 0, + OP_EXIT, + OP_GET_DEVS, + OP_OPEN, + OP_CLOSE, + OP_GET_OPTION_DESC, + OP_CTL_OPTION, + OP_GET_PARAMS, + OP_START, + OP_READ, + OP_CANCEL, + OP_SET_IO_MODE, + OP_GET_SELECT_FD, + NUM_OPS +}; + +typedef SANE_Status (*op_init_t) (SANE_Int *, SANE_Auth_Callback); +typedef void (*op_exit_t) (void); +typedef SANE_Status (*op_get_devs_t) (const SANE_Device ***, SANE_Bool); +typedef SANE_Status (*op_open_t) (SANE_String_Const, SANE_Handle *); +typedef void (*op_close_t) (SANE_Handle); +typedef const SANE_Option_Descriptor * (*op_get_option_desc_t) (SANE_Handle, + SANE_Int); +typedef SANE_Status (*op_ctl_option_t) (SANE_Handle, SANE_Int, SANE_Action, + void *, SANE_Int *); +typedef SANE_Status (*op_get_params_t) (SANE_Handle, SANE_Parameters *); +typedef SANE_Status (*op_start_t) (SANE_Handle); +typedef SANE_Status (*op_read_t) (SANE_Handle, SANE_Byte *, SANE_Int, + SANE_Int *); +typedef void (*op_cancel_t) (SANE_Handle); +typedef SANE_Status (*op_set_io_mode_t) (SANE_Handle, SANE_Bool); +typedef SANE_Status (*op_get_select_fd_t) (SANE_Handle, SANE_Int *); + +struct backend +{ + struct backend *next; + char *name; + u_int permanent:1; /* is the backend preloaded? */ + u_int loaded:1; /* are the functions available? */ + u_int inited:1; /* has the backend been initialized? */ + void *handle; /* handle returned by dlopen() */ + void *(*op[NUM_OPS]) (void); +}; + +#define BE_ENTRY(be,func) sane_##be##_##func + +#define PRELOAD_DECL(name) \ + extern SANE_Status BE_ENTRY(name,init) (SANE_Int *, SANE_Auth_Callback); \ + extern void BE_ENTRY(name,exit) (void); \ + extern SANE_Status BE_ENTRY(name,get_devices) (const SANE_Device ***, SANE_Bool); \ + extern SANE_Status BE_ENTRY(name,open) (SANE_String_Const, SANE_Handle *); \ + extern void BE_ENTRY(name,close) (SANE_Handle); \ + extern const SANE_Option_Descriptor *BE_ENTRY(name,get_option_descriptor) (SANE_Handle, SANE_Int); \ + extern SANE_Status BE_ENTRY(name,control_option) (SANE_Handle, SANE_Int, SANE_Action, void *, SANE_Int *); \ + extern SANE_Status BE_ENTRY(name,get_parameters) (SANE_Handle, SANE_Parameters *); \ + extern SANE_Status BE_ENTRY(name,start) (SANE_Handle); \ + extern SANE_Status BE_ENTRY(name,read) (SANE_Handle, SANE_Byte *, SANE_Int, SANE_Int *); \ + extern void BE_ENTRY(name,cancel) (SANE_Handle); \ + extern SANE_Status BE_ENTRY(name,set_io_mode) (SANE_Handle, SANE_Bool); \ + extern SANE_Status BE_ENTRY(name,get_select_fd) (SANE_Handle, SANE_Int *); + +#define PRELOAD_DEFN(name) \ +{ \ + 0 /* next */, #name, \ + 1 /* permanent */, \ + 1 /* loaded */, \ + 0 /* inited */, \ + 0 /* handle */, \ + { \ + BE_ENTRY(name,init), \ + BE_ENTRY(name,exit), \ + BE_ENTRY(name,get_devices), \ + BE_ENTRY(name,open), \ + BE_ENTRY(name,close), \ + BE_ENTRY(name,get_option_descriptor), \ + BE_ENTRY(name,control_option), \ + BE_ENTRY(name,get_parameters), \ + BE_ENTRY(name,start), \ + BE_ENTRY(name,read), \ + BE_ENTRY(name,cancel), \ + BE_ENTRY(name,set_io_mode), \ + BE_ENTRY(name,get_select_fd) \ + } \ +} + +#ifndef __BEOS__ +#ifdef ENABLE_PRELOAD +#include "dll-preload.h" +#else +static struct backend preloaded_backends[] = { + { 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +}; +#endif +#endif + +struct meta_scanner +{ + struct backend *be; + SANE_Handle handle; +}; + +struct alias +{ + struct alias *next; + char *oldname; + char *newname; +}; + +/* + * List of available devices, allocated by sane_get_devices, released + * by sane_exit() + */ +static SANE_Device **devlist = NULL; +static int devlist_size = 0, devlist_len = 0; + +static struct alias *first_alias; +static SANE_Auth_Callback auth_callback; +static struct backend *first_backend; + +#ifndef __BEOS__ +static const char *op_name[] = { + "init", "exit", "get_devices", "open", "close", "get_option_descriptor", + "control_option", "get_parameters", "start", "read", "cancel", + "set_io_mode", "get_select_fd" +}; +#else +static const char *op_name[] = { + "sane_init", "sane_exit", "sane_get_devices", "sane_open", "sane_close", "sane_get_option_descriptor", + "sane_control_option", "sane_get_parameters", "sane_start", "sane_read", "sane_cancel", + "sane_set_io_mode", "sane_get_select_fd" +}; +#endif /* __BEOS__ */ + +static void * +op_unsupported (void) +{ + DBG (1, "op_unsupported: call to unsupported backend operation\n"); + return (void *) (long) SANE_STATUS_UNSUPPORTED; +} + + +static SANE_Status +add_backend (const char *name, struct backend **bep) +{ + struct backend *be, *prev; + + DBG (3, "add_backend: adding backend `%s'\n", name); + + if (strcmp (name, "dll") == 0) + { + DBG (0, "add_backend: remove the dll-backend from your dll.conf!\n"); + return SANE_STATUS_GOOD; + } + + for (prev = 0, be = first_backend; be; prev = be, be = be->next) + if (strcmp (be->name, name) == 0) + { + DBG (1, "add_backend: `%s' is already there\n", name); + /* move to front so we preserve order that we'd get with + dynamic loading: */ + if (prev) + { + prev->next = be->next; + be->next = first_backend; + first_backend = be; + } + if (bep) + *bep = be; + return SANE_STATUS_GOOD; + } + + be = calloc (1, sizeof (*be)); + if (!be) + return SANE_STATUS_NO_MEM; + + be->name = strdup (name); + if (!be->name) + return SANE_STATUS_NO_MEM; + be->next = first_backend; + first_backend = be; + if (bep) + *bep = be; + return SANE_STATUS_GOOD; +} + +#if defined(HAVE_NSLINKMODULE) +static const char *dyld_get_error_str (); + +static const char * +dyld_get_error_str () +{ + NSLinkEditErrors c; + int errorNumber; + const char *fileName; + const char *errorString; + + NSLinkEditError (&c, &errorNumber, &fileName, &errorString); + return errorString; +} +#endif + +#ifdef __BEOS__ +#include <FindDirectory.h> + +static SANE_Status +load (struct backend *be) +{ + /* use BeOS kernel function to load scanner addons from ~/config/add-ons/SANE */ + char path[PATH_MAX]; + image_id id = -1; + int i, w; + directory_which which[3] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_BEOS_ADDONS_DIRECTORY }; + + /* look for config files in SANE/conf */ + for (w = 0; (w < 3) && (id < 0) && (find_directory(which[w],0,true,path,PATH_MAX) == 0); w++) + { + strcat(path,"/SANE/"); + strcat(path,be->name); + DBG(1, "loading backend %s\n", be->name); + + /* initialize all ops to "unsupported" so we can "use" the backend + even if the stuff later in this function fails */ + be->loaded = 1; + be->handle = 0; + for (i = 0; i < NUM_OPS; ++i) be->op[i] = op_unsupported; + DBG(2, "dlopen()ing `%s'\n", path); + id=load_add_on(path); + if (id < 0) + { + continue; /* try next path */ + } + be->handle=(void *)id; + + for (i = 0; i < NUM_OPS; ++i) + { + void *(*op) (); + op = NULL; + /* Look for the symbol */ + if ((get_image_symbol(id, op_name[i],B_SYMBOL_TYPE_TEXT,(void **)&op) < 0) || !op) + DBG(2, "unable to find %s\n", op_name[i]); + else be->op[i]=op; + } + } + if (id < 0) + { + DBG(2, "load: couldn't find %s\n",path); + return SANE_STATUS_INVAL; + } + return SANE_STATUS_GOOD; +} + +#else +static SANE_Status +load (struct backend *be) +{ +#ifdef HAVE_DLL + int mode = 0; + char *funcname, *src, *orig_src = 0, *dir, *path = 0; + char libname[PATH_MAX]; + int i; + int src_len; + FILE *fp = 0; + +#if defined(HAVE_DLOPEN) +# define PREFIX "libsane-" +# ifdef __hpux +# define POSTFIX ".sl.%u" +# define ALT_POSTFIX ".so.%u" +# elif defined (HAVE_WINDOWS_H) +# undef PREFIX +# define PREFIX "cygsane-" +# define POSTFIX "-%u.dll" +# elif defined (HAVE_OS2_H) +# undef PREFIX +# define PREFIX "" +# define POSTFIX ".dll" +# elif defined (__APPLE__) && defined (__MACH__) +# define POSTFIX ".%u.so" +# else +# define POSTFIX ".so.%u" +# endif + mode = getenv ("LD_BIND_NOW") ? RTLD_NOW : RTLD_LAZY; +#elif defined(HAVE_SHL_LOAD) +# define PREFIX "libsane-" +# define POSTFIX ".sl.%u" + mode = BIND_DEFERRED; +#elif defined(HAVE_NSLINKMODULE) +# define PREFIX "libsane-" +# define POSTFIX ".%u.so" + mode = NSLINKMODULE_OPTION_RETURN_ON_ERROR + NSLINKMODULE_OPTION_PRIVATE; +#else +# error "Tried to compile unsupported DLL." +#endif /* HAVE_DLOPEN */ + + /* initialize all ops to "unsupported" so we can "use" the backend + even if the stuff later in this function fails */ + be->loaded = 1; + be->handle = 0; + for (i = 0; i < NUM_OPS; ++i) + be->op[i] = op_unsupported; + + path = getenv ("LD_LIBRARY_PATH"); + if (!path) + path = getenv ("SHLIB_PATH"); /* for HP-UX */ + if (!path) + path = getenv ("LIBPATH"); /* for AIX */ + + if (path) + { + src_len = strlen (path) + strlen (STRINGIFY (LIBDIR)) + 1 + 1; + src = malloc (src_len); + if (!src) + { + DBG (1, "load: malloc failed: %s\n", strerror (errno)); + return SANE_STATUS_NO_MEM; + } + orig_src = src; + snprintf (src, src_len, "%s:%s", path, STRINGIFY (LIBDIR)); + } + else + { + src = STRINGIFY (LIBDIR); + src = strdup (src); + if (!src) + { + DBG (1, "load: strdup failed: %s\n", strerror (errno)); + return SANE_STATUS_NO_MEM; + } + } + DBG (3, "load: searching backend `%s' in `%s'\n", be->name, src); + + dir = strsep (&src, DIR_SEP); + + while (dir) + { +#ifdef HAVE_OS2_H /* only max 7.3 names work with dlopen() for DLLs on OS/2 */ + snprintf (libname, sizeof (libname), "%s/" PREFIX "%.2s%.5s" POSTFIX, + dir, be->name, strlen(be->name)>7 ? (be->name)+strlen(be->name)-5 : + (be->name)+2, V_MAJOR); +#else + snprintf (libname, sizeof (libname), "%s/" PREFIX "%s" POSTFIX, + dir, be->name, V_MAJOR); +#endif + DBG (4, "load: trying to load `%s'\n", libname); + fp = fopen (libname, "r"); + if (fp) + break; + DBG (4, "load: couldn't open `%s' (%s)\n", libname, strerror (errno)); + +#ifdef ALT_POSTFIX + /* Some platforms have two ways of storing their libraries, try both + postfixes */ + snprintf (libname, sizeof (libname), "%s/" PREFIX "%s" ALT_POSTFIX, + dir, be->name, V_MAJOR); + DBG (4, "load: trying to load `%s'\n", libname); + fp = fopen (libname, "r"); + if (fp) + break; + DBG (4, "load: couldn't open `%s' (%s)\n", libname, strerror (errno)); +#endif + + dir = strsep (&src, DIR_SEP); + } + if (orig_src) + free (orig_src); + if (!fp) + { + DBG (1, "load: couldn't find backend `%s' (%s)\n", + be->name, strerror (errno)); + return SANE_STATUS_INVAL; + } + fclose (fp); + DBG (3, "load: dlopen()ing `%s'\n", libname); + +#ifdef HAVE_DLOPEN + be->handle = dlopen (libname, mode); +#elif defined(HAVE_SHL_LOAD) + be->handle = (shl_t) shl_load (libname, mode, 0L); +#elif defined(HAVE_NSLINKMODULE) + { + NSObjectFileImage objectfile_img = NULL; + if (NSCreateObjectFileImageFromFile (libname, &objectfile_img) + == NSObjectFileImageSuccess) + { + be->handle = NSLinkModule (objectfile_img, libname, mode); + NSDestroyObjectFileImage (objectfile_img); + } + } +#else +# error "Tried to compile unsupported DLL." +#endif /* HAVE_DLOPEN */ + if (!be->handle) + { +#ifdef HAVE_DLOPEN + DBG (1, "load: dlopen() failed (%s)\n", dlerror ()); +#elif defined(HAVE_NSLINKMODULE) + DBG (1, "load: dyld error (%s)\n", dyld_get_error_str ()); +#else + DBG (1, "load: dlopen() failed (%s)\n", strerror (errno)); +#endif + return SANE_STATUS_INVAL; + } + + /* all is dandy---lookup and fill in backend ops: */ + funcname = alloca (strlen (be->name) + 64); + for (i = 0; i < NUM_OPS; ++i) + { + void *(*op) (void); + + sprintf (funcname, "_sane_%s_%s", be->name, op_name[i]); + + /* First try looking up the symbol without a leading underscore. */ +#ifdef HAVE_DLOPEN + op = (void *(*)(void)) dlsym (be->handle, funcname + 1); +#elif defined(HAVE_SHL_LOAD) + shl_findsym ((shl_t *) & (be->handle), funcname + 1, TYPE_UNDEFINED, + &op); +#elif defined(HAVE_NSLINKMODULE) + { + NSSymbol *nssym = NSLookupSymbolInModule (be->handle, funcname); + if (!nssym) + { + DBG (15, "dyld error: %s\n", dyld_get_error_str ()); + } + else + { + op = (void *(*)(void)) NSAddressOfSymbol (nssym); + } + } +#else +# error "Tried to compile unsupported DLL." +#endif /* HAVE_DLOPEN */ + if (op) + be->op[i] = op; + else + { + /* Try again, with an underscore prepended. */ +#ifdef HAVE_DLOPEN + op = (void *(*)(void)) dlsym (be->handle, funcname); +#elif defined(HAVE_SHL_LOAD) + shl_findsym (be->handle, funcname, TYPE_UNDEFINED, &op); +#elif defined(HAVE_NSLINKMODULE) + { + NSSymbol *nssym = NSLookupSymbolInModule (be->handle, funcname); + if (!nssym) + { + DBG (15, "dyld error: %s\n", dyld_get_error_str ()); + } + else + { + op = (void *(*)(void)) NSAddressOfSymbol (nssym); + } + } +#else +# error "Tried to compile unsupported DLL." +#endif /* HAVE_DLOPEN */ + if (op) + be->op[i] = op; + } + if (NULL == op) + DBG (1, "load: unable to find %s\n", funcname); + } + + return SANE_STATUS_GOOD; + +# undef PREFIX +# undef POSTFIX +#else /* HAVE_DLL */ + DBG (1, + "load: ignoring attempt to load `%s'; compiled without dl support\n", + be->name); + return SANE_STATUS_UNSUPPORTED; +#endif /* HAVE_DLL */ +} +#endif /* __BEOS__ */ + +static SANE_Status +init (struct backend *be) +{ + SANE_Status status; + SANE_Int version; + + if (!be->loaded) + { + status = load (be); + if (status != SANE_STATUS_GOOD) + return status; + } + + DBG (3, "init: initializing backend `%s'\n", be->name); + + status = (*(op_init_t)be->op[OP_INIT]) (&version, auth_callback); + if (status != SANE_STATUS_GOOD) + return status; + + if (SANE_VERSION_MAJOR (version) != SANE_CURRENT_MAJOR) + { + DBG (1, + "init: backend `%s' has a wrong major version (%d instead of %d)\n", + be->name, SANE_VERSION_MAJOR (version), SANE_CURRENT_MAJOR); + return SANE_STATUS_INVAL; + } + DBG (4, "init: backend `%s' is version %d.%d.%d\n", be->name, + SANE_VERSION_MAJOR (version), SANE_VERSION_MINOR (version), + SANE_VERSION_BUILD (version)); + + be->inited = 1; + + return SANE_STATUS_GOOD; +} + + +static void +add_alias (const char *line_param) +{ +#ifndef __BEOS__ + const char *command; + enum + { CMD_ALIAS, CMD_HIDE } + cmd; + const char *oldname, *oldend, *newname; + size_t oldlen, newlen; + struct alias *alias; + char *line; + + command = sanei_config_skip_whitespace (line_param); + if (!*command) + return; + + line = strchr (command, '#'); + if (line) + *line = '\0'; + + line = strpbrk (command, " \t"); + if (!line) + return; + *line++ = '\0'; + + if (strcmp (command, "alias") == 0) + cmd = CMD_ALIAS; + else if (strcmp (command, "hide") == 0) + cmd = CMD_HIDE; + else + return; + + newlen = 0; + newname = NULL; + if (cmd == CMD_ALIAS) + { + char *newend; + + newname = sanei_config_skip_whitespace (line); + if (!*newname) + return; + if (*newname == '\"') + { + ++newname; + newend = strchr (newname, '\"'); + } + else + newend = strpbrk (newname, " \t"); + if (!newend) + return; + + newlen = newend - newname; + line = (char *) (newend + 1); + } + + oldname = sanei_config_skip_whitespace (line); + if (!*oldname) + return; + oldend = oldname + strcspn (oldname, " \t"); + + oldlen = oldend - oldname; + + alias = malloc (sizeof (struct alias)); + if (alias) + { + alias->oldname = malloc (oldlen + newlen + 2); + if (alias->oldname) + { + strncpy (alias->oldname, oldname, oldlen); + alias->oldname[oldlen] = '\0'; + if (cmd == CMD_ALIAS) + { + alias->newname = alias->oldname + oldlen + 1; + strncpy (alias->newname, newname, newlen); + alias->newname[newlen] = '\0'; + } + else + alias->newname = NULL; + + alias->next = first_alias; + first_alias = alias; + return; + } + free (alias); + } + return; +#endif +} + + +static void +read_config (const char *conffile) +{ + FILE *fp; + char config_line[PATH_MAX]; + char *backend_name; + + fp = sanei_config_open (conffile); + if (!fp) + { + DBG (1, "sane_init/read_config: Couldn't open config file (%s): %s\n", + conffile, strerror (errno)); + return; /* don't insist on config file */ + } + + DBG (5, "sane_init/read_config: reading %s\n", conffile); + while (sanei_config_read (config_line, sizeof (config_line), fp)) + { + char *comment; + SANE_String_Const cp; + + cp = sanei_config_get_string (config_line, &backend_name); + /* ignore empty lines */ + if (!backend_name || cp == config_line) + { + if (backend_name) + free (backend_name); + continue; + } + /* ignore line comments */ + if (backend_name[0] == '#') + { + free (backend_name); + continue; + } + /* ignore comments after backend names */ + comment = strchr (backend_name, '#'); + if (comment) + *comment = '\0'; + add_backend (backend_name, 0); + free (backend_name); + } + fclose (fp); +} + +static void +read_dlld (void) +{ + DIR *dlld; + struct dirent *dllconf; + struct stat st; + char conffile[PATH_MAX], dlldir[PATH_MAX]; + size_t len, plen; + const char *dir_list; + char *copy, *next, *dir; + + dir_list = sanei_config_get_paths (); + if (!dir_list) + { + DBG(2, "sane_init/read_dlld: Unable to detect configuration directories\n"); + return; + } + + copy = strdup (dir_list); + + for (next = copy; (dir = strsep (&next, DIR_SEP)) != NULL;) + { + snprintf (dlldir, sizeof (dlldir), "%s%s", dir, "/dll.d"); + + DBG(4, "sane_init/read_dlld: attempting to open directory `%s'\n", dlldir); + + dlld = opendir (dlldir); + if (dlld) + { + /* length of path to parent dir of dll.d/ */ + plen = strlen (dir) + 1; + + DBG(3, "sane_init/read_dlld: using config directory `%s'\n", dlldir); + break; + } + } + free (copy); + + if (dlld == NULL) + { + DBG (1, "sane_init/read_dlld: opendir failed: %s\n", + strerror (errno)); + return; + } + + while ((dllconf = readdir (dlld)) != NULL) + { + /* dotfile (or directory) */ + if (dllconf->d_name[0] == '.') + continue; + + len = strlen (dllconf->d_name); + + /* backup files */ + if ((dllconf->d_name[len-1] == '~') + || (dllconf->d_name[len-1] == '#')) + continue; + + snprintf (conffile, PATH_MAX, "%s/%s", dlldir, dllconf->d_name); + + DBG (5, "sane_init/read_dlld: considering %s\n", conffile); + + if (stat (conffile, &st) != 0) + continue; + + if (!S_ISREG (st.st_mode)) + continue; + + /* expects a path relative to PATH_SANE_CONFIG_DIR */ + read_config (conffile+plen); + } + + closedir (dlld); + + DBG (5, "sane_init/read_dlld: done.\n"); +} + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ +#ifndef __BEOS__ + char config_line[PATH_MAX]; + size_t len; + FILE *fp; + int i; +#else + DIR *dir; + struct dirent *dirent; + char path[1024]; + directory_which which[3] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_BEOS_ADDONS_DIRECTORY }; + int i; +#endif + + DBG_INIT (); + + auth_callback = authorize; + + DBG (1, "sane_init: SANE dll backend version %s from %s\n", DLL_VERSION, + PACKAGE_STRING); + +#ifndef __BEOS__ + /* chain preloaded backends together: */ + for (i = 0; i < NELEMS (preloaded_backends); ++i) + { + if (!preloaded_backends[i].name) + continue; + DBG (3, "sane_init: adding backend `%s' (preloaded)\n", preloaded_backends[i].name); + preloaded_backends[i].next = first_backend; + first_backend = &preloaded_backends[i]; + } + + /* Return the version number of the sane-backends package to allow + the frontend to print them. This is done only for net and dll, + because these backends are usually called by the frontend. */ + if (version_code) + *version_code = SANE_VERSION_CODE (SANE_DLL_V_MAJOR, SANE_DLL_V_MINOR, + SANE_DLL_V_BUILD); + + /* + * Read dll.conf & dll.d + * Read dll.d first, so that the extras backends will be tried last + */ + read_dlld (); + read_config (DLL_CONFIG_FILE); + + fp = sanei_config_open (DLL_ALIASES_FILE); + if (!fp) + return SANE_STATUS_GOOD; /* don't insist on aliases file */ + + DBG (5, "sane_init: reading %s\n", DLL_ALIASES_FILE); + while (sanei_config_read (config_line, sizeof (config_line), fp)) + { + if (config_line[0] == '#') /* ignore line comments */ + continue; + + len = strlen (config_line); + if (!len) + continue; /* ignore empty lines */ + + add_alias (config_line); + } + fclose (fp); + +#else + /* no ugly config files, just get scanners from their ~/config/add-ons/SANE */ + /* look for drivers */ + for (i = 0; i < 3; i++) + { + if (find_directory(which[i],0,true,path,1024) < B_OK) + continue; + strcat(path,"/SANE/"); + dir=opendir(path); + if(!dir) continue; + + while((dirent=readdir(dir))) + { + if((strcmp(dirent->d_name,".")==0) || (strcmp(dirent->d_name,"..")==0)) continue; + if((strcmp(dirent->d_name,"dll")==0)) continue; + add_backend(dirent->d_name,0); + } + closedir(dir); + } +#endif /* __BEOS__ */ + + return SANE_STATUS_GOOD; +} + +void +sane_exit (void) +{ + struct backend *be, *next; + struct alias *alias; + + DBG (2, "sane_exit: exiting\n"); + + for (be = first_backend; be; be = next) + { + next = be->next; + if (be->loaded) + { + if (be->inited) + { + DBG (3, "sane_exit: calling backend `%s's exit function\n", + be->name); + (*(op_exit_t)be->op[OP_EXIT]) (); + } +#ifdef __BEOS__ + /* use BeOS kernel functions to unload add-ons */ + if(be->handle) unload_add_on((image_id)be->handle); +#else +#ifdef HAVE_DLL + +#ifdef HAVE_DLOPEN + if (be->handle) + dlclose (be->handle); +#elif defined(HAVE_SHL_LOAD) + if (be->handle) + shl_unload (be->handle); +#elif defined(HAVE_NSLINKMODULE) + if (be->handle) + NSUnLinkModule (be->handle, NSUNLINKMODULE_OPTION_NONE +# ifdef __ppc__ + | NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES +# endif + ); +#else +# error "Tried to compile unsupported DLL." +#endif /* HAVE_DLOPEN */ + +#endif /* HAVE_DLL */ +#endif /* __BEOS__ */ + } + if (!be->permanent) + { + if (be->name) + free ((void *) be->name); + free (be); + } + else + { + be->inited = 0; + } + } + first_backend = 0; + + while ((alias = first_alias) != NULL) + { + first_alias = first_alias->next; + free (alias->oldname); + free (alias); + } + + if (NULL != devlist) + { /* Release memory allocated by sane_get_devices(). */ + int i = 0; + while (devlist[i]) + free (devlist[i++]); + free (devlist); + + devlist = NULL; + devlist_size = 0; + devlist_len = 0; + } + DBG (3, "sane_exit: finished\n"); +} + +/* Note that a call to get_devices() implies that we'll have to load + all backends. To avoid this, you can call sane_open() directly + (assuming you know the name of the backend/device). This is + appropriate for the command-line interface of SANE, for example. + */ +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + const SANE_Device **be_list; + struct backend *be; + SANE_Status status; + char *full_name; + int i, num_devs; + size_t len; +#define ASSERT_SPACE(n) \ + { \ + if (devlist_len + (n) > devlist_size) \ + { \ + devlist_size += (n) + 15; \ + if (devlist) \ + devlist = realloc (devlist, devlist_size * sizeof (devlist[0])); \ + else \ + devlist = malloc (devlist_size * sizeof (devlist[0])); \ + if (!devlist) \ + return SANE_STATUS_NO_MEM; \ + } \ + } + + DBG (3, "sane_get_devices\n"); + + if (devlist) + for (i = 0; i < devlist_len; ++i) + free ((void *) devlist[i]); + devlist_len = 0; + + for (be = first_backend; be; be = be->next) + { + if (!be->inited) + if (init (be) != SANE_STATUS_GOOD) + continue; + + status = (*(op_get_devs_t)be->op[OP_GET_DEVS]) (&be_list, local_only); + if (status != SANE_STATUS_GOOD || !be_list) + continue; + + /* count the number of devices for this backend: */ + for (num_devs = 0; be_list[num_devs]; ++num_devs); + + ASSERT_SPACE (num_devs); + + for (i = 0; i < num_devs; ++i) + { + SANE_Device *dev; + char *mem; + struct alias *alias; + + for (alias = first_alias; alias != NULL; alias = alias->next) + { + len = strlen (be->name); + if (strlen (alias->oldname) <= len) + continue; + if (strncmp (alias->oldname, be->name, len) == 0 + && alias->oldname[len] == ':' + && strcmp (&alias->oldname[len + 1], be_list[i]->name) == 0) + break; + } + + if (alias) + { + if (!alias->newname) /* hidden device */ + continue; + + len = strlen (alias->newname); + mem = malloc (sizeof (*dev) + len + 1); + if (!mem) + return SANE_STATUS_NO_MEM; + + full_name = mem + sizeof (*dev); + strcpy (full_name, alias->newname); + } + else + { + /* create a new device entry with a device name that is the + sum of the backend name a colon and the backend's device + name: */ + len = strlen (be->name) + 1 + strlen (be_list[i]->name); + mem = malloc (sizeof (*dev) + len + 1); + if (!mem) + return SANE_STATUS_NO_MEM; + + full_name = mem + sizeof (*dev); + strcpy (full_name, be->name); + strcat (full_name, ":"); + strcat (full_name, be_list[i]->name); + } + + dev = (SANE_Device *) mem; + dev->name = full_name; + dev->vendor = be_list[i]->vendor; + dev->model = be_list[i]->model; + dev->type = be_list[i]->type; + + devlist[devlist_len++] = dev; + } + } + + /* terminate device list with NULL entry: */ + ASSERT_SPACE (1); + devlist[devlist_len++] = 0; + + *device_list = (const SANE_Device **) devlist; + DBG (3, "sane_get_devices: found %d devices\n", devlist_len - 1); + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) +{ + const char *be_name, *dev_name; + struct meta_scanner *s; + SANE_Handle handle; + struct backend *be; + SANE_Status status; + struct alias *alias; + + DBG (3, "sane_open: trying to open `%s'\n", full_name); + + for (alias = first_alias; alias != NULL; alias = alias->next) + { + if (!alias->newname) + continue; + if (strcmp (alias->newname, full_name) == 0) + { + full_name = alias->oldname; + break; + } + } + + dev_name = strchr (full_name, ':'); + if (dev_name) + { +#ifdef strndupa + be_name = strndupa (full_name, dev_name - full_name); +#else + char *tmp; + + tmp = alloca (dev_name - full_name + 1); + memcpy (tmp, full_name, dev_name - full_name); + tmp[dev_name - full_name] = '\0'; + be_name = tmp; +#endif + ++dev_name; /* skip colon */ + } + else + { + /* if no colon interpret full_name as the backend name; an empty + backend device name will cause us to open the first device of + that backend. */ + be_name = full_name; + dev_name = ""; + } + + if (!be_name[0]) + be = first_backend; + else + for (be = first_backend; be; be = be->next) + if (strcmp (be->name, be_name) == 0) + break; + + if (!be) + { + status = add_backend (be_name, &be); + if (status != SANE_STATUS_GOOD) + return status; + } + + if (!be->inited) + { + status = init (be); + if (status != SANE_STATUS_GOOD) + return status; + } + + status = (*(op_open_t)be->op[OP_OPEN]) (dev_name, &handle); + if (status != SANE_STATUS_GOOD) + return status; + + s = calloc (1, sizeof (*s)); + if (!s) + return SANE_STATUS_NO_MEM; + + s->be = be; + s->handle = handle; + *meta_handle = s; + + DBG (3, "sane_open: open successful\n"); + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_close(handle=%p)\n", handle); + (*(op_close_t)s->be->op[OP_CLOSE]) (s->handle); + free (s); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_get_option_descriptor(handle=%p,option=%d)\n", handle, + option); + return (*(op_get_option_desc_t)s->be->op[OP_GET_OPTION_DESC]) (s->handle, option); +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *value, SANE_Word * info) +{ + struct meta_scanner *s = handle; + + DBG (3, + "sane_control_option(handle=%p,option=%d,action=%d,value=%p,info=%p)\n", + handle, option, action, value, (void *) info); + return (*(op_ctl_option_t)s->be->op[OP_CTL_OPTION]) (s->handle, option, action, value, + info); +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_get_parameters(handle=%p,params=%p)\n", handle, (void *) params); + return (*(op_get_params_t)s->be->op[OP_GET_PARAMS]) (s->handle, params); +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_start(handle=%p)\n", handle); + return (*(op_start_t)s->be->op[OP_START]) (s->handle); +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, + SANE_Int * length) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_read(handle=%p,data=%p,maxlen=%d,lenp=%p)\n", + handle, data, max_length, (void *) length); + return (*(op_read_t)s->be->op[OP_READ]) (s->handle, data, max_length, length); +} + +void +sane_cancel (SANE_Handle handle) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_cancel(handle=%p)\n", handle); + (*(op_cancel_t)s->be->op[OP_CANCEL]) (s->handle); +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_set_io_mode(handle=%p,nonblocking=%d)\n", handle, + non_blocking); + return (*(op_set_io_mode_t)s->be->op[OP_SET_IO_MODE]) (s->handle, non_blocking); +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + struct meta_scanner *s = handle; + + DBG (3, "sane_get_select_fd(handle=%p,fdp=%p)\n", handle, (void *) fd); + return (*(op_get_select_fd_t)s->be->op[OP_GET_SELECT_FD]) (s->handle, fd); +} |