summaryrefslogtreecommitdiff
path: root/backend/pnm.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/pnm.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/pnm.c')
-rw-r--r--backend/pnm.c1395
1 files changed, 1395 insertions, 0 deletions
diff --git a/backend/pnm.c b/backend/pnm.c
new file mode 100644
index 0000000..88b6e4a
--- /dev/null
+++ b/backend/pnm.c
@@ -0,0 +1,1395 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 Andreas Beck
+ Copyright (C) 2000, 2001 Michael Herder <crapsite@gmx.net>
+ Copyright (C) 2001, 2002 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2008 Stéphane Voltz <stef.dev@free.fr>
+ 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. */
+
+#define BUILD 9
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME pnm
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MAGIC (void *)0xab730324
+
+static int is_open = 0;
+static int rgb_comp = 0;
+static int three_pass = 0;
+static int hand_scanner = 0;
+static int pass = 0;
+static char filename[PATH_MAX] = "/tmp/input.ppm";
+static SANE_Word status_none = SANE_TRUE;
+static SANE_Word status_eof = SANE_FALSE;
+static SANE_Word status_jammed = SANE_FALSE;
+static SANE_Word status_nodocs = SANE_FALSE;
+static SANE_Word status_coveropen = SANE_FALSE;
+static SANE_Word status_ioerror = SANE_FALSE;
+static SANE_Word status_nomem = SANE_FALSE;
+static SANE_Word status_accessdenied = SANE_FALSE;
+static SANE_Word test_option = 0;
+#ifdef SANE_STATUS_WARMING_UP
+static SANE_Word warming_up = SANE_FALSE;
+static struct timeval start;
+#endif
+
+static SANE_Fixed bright = 0;
+static SANE_Word res = 75;
+static SANE_Fixed contr = 0;
+static SANE_Bool gray = SANE_FALSE;
+static SANE_Bool usegamma = SANE_FALSE;
+static SANE_Word gamma[4][256];
+static enum
+{
+ ppm_bitmap,
+ ppm_greyscale,
+ ppm_color
+}
+ppm_type = ppm_color;
+static FILE *infile = NULL;
+static const SANE_Word resbit_list[] = {
+ 17,
+ 75, 90, 100, 120, 135, 150, 165, 180, 195,
+ 200, 210, 225, 240, 255, 270, 285, 300
+};
+static const SANE_Range percentage_range = {
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 0 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+typedef enum
+{
+ opt_num_opts = 0,
+ opt_source_group,
+ opt_filename,
+ opt_resolution,
+ opt_enhancement_group,
+ opt_brightness,
+ opt_contrast,
+ opt_grayify,
+ opt_three_pass,
+ opt_hand_scanner,
+ opt_default_enhancements,
+ opt_read_only,
+ opt_gamma_group,
+ opt_custom_gamma,
+ opt_gamma,
+ opt_gamma_r,
+ opt_gamma_g,
+ opt_gamma_b,
+ opt_status_group,
+ opt_status,
+ opt_status_eof,
+ opt_status_jammed,
+ opt_status_nodocs,
+ opt_status_coveropen,
+ opt_status_ioerror,
+ opt_status_nomem,
+ opt_status_accessdenied,
+
+ /* must come last: */
+ num_options
+}
+pnm_opts;
+
+static SANE_Option_Descriptor sod[] = {
+ { /* opt_num_opts */
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_source_group */
+ "",
+ SANE_I18N ("Source Selection"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_filename */
+ SANE_NAME_FILE,
+ SANE_TITLE_FILE,
+ SANE_DESC_FILE,
+ SANE_TYPE_STRING,
+ SANE_UNIT_NONE,
+ sizeof (filename),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ {
+ /* opt_resolution */
+ SANE_NAME_SCAN_RESOLUTION,
+ SANE_TITLE_SCAN_RESOLUTION,
+ SANE_DESC_SCAN_RESOLUTION,
+ SANE_TYPE_INT,
+ SANE_UNIT_DPI,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC,
+ SANE_CONSTRAINT_WORD_LIST,
+ {(SANE_String_Const *) resbit_list}
+ }
+ ,
+ { /* opt_enhancement_group */
+ "",
+ SANE_I18N ("Image Enhancement"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_brightness */
+ SANE_NAME_BRIGHTNESS,
+ SANE_TITLE_BRIGHTNESS,
+ SANE_DESC_BRIGHTNESS,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_PERCENT,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & percentage_range} /* this is ANSI conformant! */
+ }
+ ,
+ { /* opt_contrast */
+ SANE_NAME_CONTRAST,
+ SANE_TITLE_CONTRAST,
+ SANE_DESC_CONTRAST,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_PERCENT,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & percentage_range} /* this is ANSI conformant! */
+ }
+ ,
+ { /* opt_grayify */
+ "grayify",
+ SANE_I18N ("Grayify"),
+ SANE_I18N ("Load the image as grayscale."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_three_pass */
+ "three-pass",
+ SANE_I18N ("Three-Pass Simulation"),
+ SANE_I18N
+ ("Simulate a three-pass scanner by returning 3 separate frames. "
+ "For kicks, it returns green, then blue, then red."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_hand_scanner */
+ "hand-scanner",
+ SANE_I18N ("Hand-Scanner Simulation"),
+ SANE_I18N ("Simulate a hand-scanner. Hand-scanners often do not know the "
+ "image height a priori. Instead, they return a height of -1. "
+ "Setting this option allows to test whether a frontend can "
+ "handle this correctly."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_default_enhancements */
+ "default-enhancements",
+ SANE_I18N ("Defaults"),
+ SANE_I18N ("Set default values for enhancement controls (brightness & "
+ "contrast)."),
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_read_only */
+ "read-only",
+ SANE_I18N ("Read only test-option"),
+ SANE_I18N ("Let's see whether frontends can treat this right"),
+ SANE_TYPE_INT,
+ SANE_UNIT_PERCENT,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_gamma_group */
+ "",
+ SANE_I18N ("Gamma Tables"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_custom_gamma */
+ SANE_NAME_CUSTOM_GAMMA,
+ SANE_TITLE_CUSTOM_GAMMA,
+ SANE_DESC_CUSTOM_GAMMA,
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_gamma */
+ SANE_NAME_GAMMA_VECTOR,
+ SANE_TITLE_GAMMA_VECTOR,
+ SANE_DESC_GAMMA_VECTOR,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_gamma_r */
+ SANE_NAME_GAMMA_VECTOR_R,
+ SANE_TITLE_GAMMA_VECTOR_R,
+ SANE_DESC_GAMMA_VECTOR_R,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_gamma_g */
+ SANE_NAME_GAMMA_VECTOR_G,
+ SANE_TITLE_GAMMA_VECTOR_G,
+ SANE_DESC_GAMMA_VECTOR_G,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_gamma_b */
+ SANE_NAME_GAMMA_VECTOR_B,
+ SANE_TITLE_GAMMA_VECTOR_B,
+ SANE_DESC_GAMMA_VECTOR_B,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_status_group */
+ "",
+ SANE_I18N ("Status Code Simulation"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status */
+ "status",
+ SANE_I18N ("Do not force status code"),
+ SANE_I18N ("Do not force the backend to return a status code."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_eof */
+ "status-eof",
+ SANE_I18N ("Return SANE_STATUS_EOF"),
+ SANE_I18N ("Force the backend to return the status code SANE_STATUS_EOF "
+ "after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_jammed */
+ "status-jammed",
+ SANE_I18N ("Return SANE_STATUS_JAMMED"),
+ SANE_I18N
+ ("Force the backend to return the status code SANE_STATUS_JAMMED "
+ "after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_nodocs */
+ "status-nodocs",
+ SANE_I18N ("Return SANE_STATUS_NO_DOCS"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_NO_DOCS after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_coveropen */
+ "status-coveropen",
+ SANE_I18N ("Return SANE_STATUS_COVER_OPEN"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_COVER_OPEN after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_ioerror */
+ "status-ioerror",
+ SANE_I18N ("Return SANE_STATUS_IO_ERROR"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_IO_ERROR after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_nomem */
+ "status-nomem",
+ SANE_I18N ("Return SANE_STATUS_NO_MEM"),
+ SANE_I18N
+ ("Force the backend to return the status code SANE_STATUS_NO_MEM "
+ "after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_accessdenied */
+ "status-accessdenied",
+ SANE_I18N ("Return SANE_STATUS_ACCESS_DENIED"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_ACCESS_DENIED after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+};
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+/* This library is a demo implementation of a SANE backend. It
+ implements a virtual device, a PNM file-filter. */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+
+ DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+ version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+ DBG (1, "sane_init: SANE pnm backend version %d.%d.%d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ DBG (2, "sane_exit\n");
+ return;
+}
+
+/* Device select/open/close */
+
+static const SANE_Device dev[] = {
+ {
+ "0",
+ "Noname",
+ "PNM file reader",
+ "virtual device"},
+ {
+ "1",
+ "Noname",
+ "PNM file reader",
+ "virtual device"},
+#ifdef SANE_STATUS_HW_LOCKED
+ {
+ "locked",
+ "Noname",
+ "Hardware locked",
+ "virtual device"},
+#endif
+#ifdef SANE_STATUS_WARMING_UP
+ {
+ "warmup",
+ "Noname",
+ "Always warming up",
+ "virtual device"},
+#endif
+};
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device *devlist[] = {
+ dev + 0, dev + 1,
+#ifdef SANE_STATUS_HW_LOCKED
+ dev + 2,
+#endif
+#ifdef SANE_STATUS_WARMING_UP
+ dev + 3,
+#endif
+ 0
+ };
+
+ DBG (2, "sane_get_devices: local_only = %d\n", local_only);
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ int i;
+
+ if (!devicename)
+ return SANE_STATUS_INVAL;
+ DBG (2, "sane_open: devicename = \"%s\"\n", devicename);
+
+ if (!devicename[0])
+ i = 0;
+ else
+ for (i = 0; i < NELEMS (dev); ++i)
+ if (strcmp (devicename, dev[i].name) == 0)
+ break;
+ if (i >= NELEMS (dev))
+ return SANE_STATUS_INVAL;
+
+ if (is_open)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ is_open = 1;
+ *handle = MAGIC;
+ for (i = 0; i < 256; i++)
+ {
+ gamma[0][i] = i;
+ gamma[1][i] = i;
+ gamma[2][i] = i;
+ gamma[3][i] = i;
+ }
+
+#ifdef SANE_STATUS_HW_LOCKED
+ if(strncmp(devicename,"locked",6)==0)
+ return SANE_STATUS_HW_LOCKED;
+#endif
+
+#ifdef SANE_STATUS_WARMING_UP
+ if(strncmp(devicename,"warmup",6)==0)
+ {
+ warming_up = SANE_TRUE;
+ start.tv_sec = 0;
+ }
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (2, "sane_close\n");
+ if (handle == MAGIC)
+ is_open = 0;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ DBG (2, "sane_get_option_descriptor: option = %d\n", option);
+ if (handle != MAGIC || !is_open)
+ return NULL; /* wrong device */
+ if (option < 0 || option >= NELEMS (sod))
+ return NULL;
+ return &sod[option];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Int myinfo = 0;
+ SANE_Status status;
+ int v;
+ v = 75;
+
+ DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ handle, option, action, value, info);
+
+ if (handle != MAGIC || !is_open)
+ {
+ DBG (1, "sane_control_option: unknown handle or not open\n");
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+ }
+
+ if (option < 0 || option >= NELEMS (sod))
+ {
+ DBG (1, "sane_control_option: option %d < 0 or >= number of options\n",
+ option);
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap))
+ {
+ DBG (4, "sane_control_option: option is inactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ if (!SANE_OPTION_IS_SETTABLE (sod[option].cap))
+ {
+ DBG (4, "sane_control_option: option is not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = sanei_constrain_value (sod + option, (void *) &v, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ switch (option)
+ {
+ case opt_resolution:
+ res = 75;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (sod[option].cap))
+ {
+ DBG (4, "sane_control_option: option is not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = sanei_constrain_value (sod + option, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ switch (option)
+ {
+ case opt_filename:
+ if ((strlen (value) + 1) > sizeof (filename))
+ return SANE_STATUS_NO_MEM;
+ strcpy (filename, value);
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case opt_resolution:
+ res = *(SANE_Word *) value;
+ break;
+ case opt_brightness:
+ bright = *(SANE_Word *) value;
+ break;
+ case opt_contrast:
+ contr = *(SANE_Word *) value;
+ break;
+ case opt_grayify:
+ gray = !!*(SANE_Word *) value;
+ if (usegamma)
+ {
+ if (gray)
+ {
+ sod[opt_gamma].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_three_pass:
+ three_pass = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case opt_hand_scanner:
+ hand_scanner = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case opt_default_enhancements:
+ bright = contr = 0;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_custom_gamma:
+ usegamma = *(SANE_Word *) value;
+ /* activate/deactivate gamma */
+ if (usegamma)
+ {
+ test_option = 100;
+ if (gray)
+ {
+ sod[opt_gamma].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ test_option = 0;
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma:
+ memcpy (&gamma[0][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma_r:
+ memcpy (&gamma[1][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma_g:
+ memcpy (&gamma[2][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma_b:
+ memcpy (&gamma[3][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ /* status */
+ case opt_status:
+ status_none = *(SANE_Word *) value;
+ if (status_none)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_eof:
+ status_eof = *(SANE_Word *) value;
+ if (status_eof)
+ {
+ status_none = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_jammed:
+ status_jammed = *(SANE_Word *) value;
+ if (status_jammed)
+ {
+ status_eof = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_nodocs:
+ status_nodocs = *(SANE_Word *) value;
+ if (status_nodocs)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_coveropen:
+ status_coveropen = *(SANE_Word *) value;
+ if (status_coveropen)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_ioerror:
+ status_ioerror = *(SANE_Word *) value;
+ if (status_ioerror)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_nomem:
+ status_nomem = *(SANE_Word *) value;
+ if (status_nomem)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_accessdenied:
+ status_accessdenied = *(SANE_Word *) value;
+ if (status_accessdenied)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_none = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ case opt_num_opts:
+ *(SANE_Word *) value = NELEMS (sod);
+ break;
+ case opt_filename:
+ strcpy (value, filename);
+ break;
+ case opt_resolution:
+ *(SANE_Word *) value = res;
+ break;
+ case opt_brightness:
+ *(SANE_Word *) value = bright;
+ break;
+ case opt_contrast:
+ *(SANE_Word *) value = contr;
+ break;
+ case opt_grayify:
+ *(SANE_Word *) value = gray;
+ break;
+ case opt_three_pass:
+ *(SANE_Word *) value = three_pass;
+ break;
+ case opt_hand_scanner:
+ *(SANE_Word *) value = hand_scanner;
+ break;
+ case opt_read_only:
+ *(SANE_Word *) value = test_option;
+ break;
+ case opt_custom_gamma:
+ *(SANE_Word *) value = usegamma;
+ break;
+ case opt_gamma:
+ memcpy ((SANE_Word *) value, &gamma[0][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_gamma_r:
+ memcpy ((SANE_Word *) value, &gamma[1][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_gamma_g:
+ memcpy ((SANE_Word *) value, &gamma[2][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_gamma_b:
+ memcpy ((SANE_Word *) value, &gamma[3][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_status:
+ *(SANE_Word *) value = status_none;
+ break;
+ case opt_status_eof:
+ *(SANE_Word *) value = status_eof;
+ break;
+ case opt_status_jammed:
+ *(SANE_Word *) value = status_jammed;
+ break;
+ case opt_status_nodocs:
+ *(SANE_Word *) value = status_nodocs;
+ break;
+ case opt_status_coveropen:
+ *(SANE_Word *) value = status_coveropen;
+ break;
+ case opt_status_ioerror:
+ *(SANE_Word *) value = status_ioerror;
+ break;
+ case opt_status_nomem:
+ *(SANE_Word *) value = status_nomem;
+ break;
+ case opt_status_accessdenied:
+ *(SANE_Word *) value = status_accessdenied;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ }
+ if (info)
+ *info = myinfo;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+get_line (char *buf, int len, FILE * f)
+{
+ do
+ fgets (buf, len, f);
+ while (*buf == '#');
+}
+
+static int
+getparmfromfile (void)
+{
+ FILE *fn;
+ int x, y;
+ char buf[1024];
+
+ parms.depth = 8;
+ parms.bytes_per_line = parms.pixels_per_line = parms.lines = 0;
+ if ((fn = fopen (filename, "rb")) == NULL)
+ {
+ DBG (1, "getparmfromfile: unable to open file \"%s\"\n", filename);
+ return -1;
+ }
+
+ /* Skip comments. */
+ do
+ get_line (buf, sizeof (buf), fn);
+ while (*buf == '#');
+ if (!strncmp (buf, "P4", 2))
+ {
+ /* Binary monochrome. */
+ parms.depth = 1;
+ ppm_type = ppm_bitmap;
+ }
+ else if (!strncmp (buf, "P5", 2))
+ {
+ /* Grayscale. */
+ parms.depth = 8;
+ ppm_type = ppm_greyscale;
+ }
+ else if (!strncmp (buf, "P6", 2))
+ {
+ /* Color. */
+ parms.depth = 8;
+ ppm_type = ppm_color;
+ }
+ else
+ {
+ DBG (1, "getparmfromfile: %s is not a recognized PPM\n", filename);
+ fclose (fn);
+ return -1;
+ }
+
+ /* Skip comments. */
+ do
+ get_line (buf, sizeof (buf), fn);
+ while (*buf == '#');
+ sscanf (buf, "%d %d", &x, &y);
+
+ parms.last_frame = SANE_TRUE;
+ parms.bytes_per_line = (ppm_type == ppm_bitmap) ? (x + 7) / 8 : x;
+ parms.pixels_per_line = x;
+ if (hand_scanner)
+ parms.lines = -1;
+ else
+ parms.lines = y;
+ if ((ppm_type == ppm_greyscale) || (ppm_type == ppm_bitmap) || gray)
+ parms.format = SANE_FRAME_GRAY;
+ else
+ {
+ if (three_pass)
+ {
+ parms.format = SANE_FRAME_RED + (pass + 1) % 3;
+ parms.last_frame = (pass >= 2);
+ }
+ else
+ {
+ parms.format = SANE_FRAME_RGB;
+ parms.bytes_per_line *= 3;
+ }
+ }
+ fclose (fn);
+ return 0;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int rc = SANE_STATUS_GOOD;
+
+ DBG (2, "sane_get_parameters\n");
+ if (handle != MAGIC || !is_open)
+ rc = SANE_STATUS_INVAL; /* Unknown handle ... */
+ else if (getparmfromfile ())
+ rc = SANE_STATUS_INVAL;
+ *params = parms;
+ return rc;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char buf[1024];
+ int nlines;
+#ifdef SANE_STATUS_WARMING_UP
+ struct timeval current;
+#endif
+
+ DBG (2, "sane_start\n");
+ rgb_comp = 0;
+ if (handle != MAGIC || !is_open)
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+#ifdef SANE_STATUS_WARMING_UP
+ if(warming_up == SANE_TRUE)
+ {
+ gettimeofday(&current,NULL);
+ if(current.tv_sec-start.tv_sec>5)
+ {
+ start.tv_sec = current.tv_sec;
+ return SANE_STATUS_WARMING_UP;
+ }
+ if(current.tv_sec-start.tv_sec<5)
+ return SANE_STATUS_WARMING_UP;
+ }
+#endif
+
+ if (infile != NULL)
+ {
+ fclose (infile);
+ infile = NULL;
+ if (!three_pass || ++pass >= 3)
+ return SANE_STATUS_EOF;
+ }
+
+ if (getparmfromfile ())
+ return SANE_STATUS_INVAL;
+
+ if ((infile = fopen (filename, "rb")) == NULL)
+ {
+ DBG (1, "sane_start: unable to open file \"%s\"\n", filename);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Skip the header (only two lines for a bitmap). */
+ nlines = (ppm_type == ppm_bitmap) ? 1 : 0;
+ while (nlines < 3)
+ {
+ /* Skip comments. */
+ get_line (buf, sizeof (buf), infile);
+ if (*buf != '#')
+ nlines++;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Int rgblength = 0;
+static SANE_Byte *rgbbuf = 0;
+static SANE_Byte rgbleftover[3] = { 0, 0, 0 };
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ int len, x, hlp;
+
+ DBG (2, "sane_read: max_length = %d, rgbleftover = {%d, %d, %d}\n",
+ max_length, rgbleftover[0], rgbleftover[1], rgbleftover[2]);
+ if (!length)
+ {
+ DBG (1, "sane_read: length == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+ *length = 0;
+ if (!data)
+ {
+ DBG (1, "sane_read: data == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (handle != MAGIC)
+ {
+ DBG (1, "sane_read: unknown handle\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!is_open)
+ {
+ DBG (1, "sane_read: call sane_open first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!infile)
+ {
+ DBG (1, "sane_read: scan was cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ if (feof (infile))
+ {
+ DBG (2, "sane_read: EOF reached\n");
+ return SANE_STATUS_EOF;
+ }
+
+ if (status_jammed == SANE_TRUE)
+ return SANE_STATUS_JAMMED;
+ if (status_eof == SANE_TRUE)
+ return SANE_STATUS_EOF;
+ if (status_nodocs == SANE_TRUE)
+ return SANE_STATUS_NO_DOCS;
+ if (status_coveropen == SANE_TRUE)
+ return SANE_STATUS_COVER_OPEN;
+ if (status_ioerror == SANE_TRUE)
+ return SANE_STATUS_IO_ERROR;
+ if (status_nomem == SANE_TRUE)
+ return SANE_STATUS_NO_MEM;
+ if (status_accessdenied == SANE_TRUE)
+ return SANE_STATUS_ACCESS_DENIED;
+
+ /* Allocate a buffer for the RGB values. */
+ if (ppm_type == ppm_color && (gray || three_pass))
+ {
+ SANE_Byte *p, *q, *rgbend;
+ if (rgbbuf == 0 || rgblength < 3 * max_length)
+ {
+ /* Allocate a new rgbbuf. */
+ free (rgbbuf);
+ rgblength = 3 * max_length;
+ rgbbuf = malloc (rgblength);
+ if (rgbbuf == 0)
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ rgblength = 3 * max_length;
+
+ /* Copy any leftovers into the buffer. */
+ q = rgbbuf;
+ p = rgbleftover + 1;
+ while (p - rgbleftover <= rgbleftover[0])
+ *q++ = *p++;
+
+ /* Slurp in the RGB buffer. */
+ len = fread (q, 1, rgblength - rgbleftover[0], infile);
+ rgbend = rgbbuf + len;
+
+ q = data;
+ if (gray)
+ {
+ /* Zip through the buffer, converting color data to grayscale. */
+ for (p = rgbbuf; p < rgbend; p += 3)
+ *q++ = ((long) p[0] + p[1] + p[2]) / 3;
+ }
+ else
+ {
+ /* Zip through the buffer, extracting data for this pass. */
+ for (p = (rgbbuf + (pass + 1) % 3); p < rgbend; p += 3)
+ *q++ = *p;
+ }
+
+ /* Save any leftovers in the array. */
+ rgbleftover[0] = len % 3;
+ p = rgbbuf + (len - rgbleftover[0]);
+ q = rgbleftover + 1;
+ while (p < rgbend)
+ *q++ = *p++;
+
+ len /= 3;
+ }
+ else
+ /* Suck in as much of the file as possible, since it's already in the
+ correct format. */
+ len = fread (data, 1, max_length, infile);
+
+ if (len == 0)
+ {
+ if (feof (infile))
+ {
+ DBG (2, "sane_read: EOF reached\n");
+ return SANE_STATUS_EOF;
+ }
+ else
+ {
+ DBG (1, "sane_read: error while reading file (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (parms.depth == 8)
+ {
+ /* Do the transformations ... DEMO ONLY ! THIS MAKES NO SENSE ! */
+ for (x = 0; x < len; x++)
+ {
+ hlp = *((unsigned char *) data + x) - 128;
+ hlp *= (contr + (100 << SANE_FIXED_SCALE_SHIFT));
+ hlp /= 100 << SANE_FIXED_SCALE_SHIFT;
+ hlp += (bright >> SANE_FIXED_SCALE_SHIFT) + 128;
+ if (hlp < 0)
+ hlp = 0;
+ if (hlp > 255)
+ hlp = 255;
+ *(data + x) = hlp;
+ }
+ /*gamma */
+ if (usegamma)
+ {
+ unsigned char uc;
+ if (gray)
+ {
+ for (x = 0; x < len; x++)
+ {
+ uc = *((unsigned char *) data + x);
+ uc = gamma[0][uc];
+ *(data + x) = uc;
+ }
+ }
+ else
+ {
+ for (x = 0; x < len; x++)
+ {
+ if (parms.format == SANE_FRAME_RGB)
+ {
+ uc = *((unsigned char *) (data + x));
+ uc = (unsigned char) gamma[rgb_comp + 1][(int) uc];
+ *((unsigned char *) data + x) = uc;
+ rgb_comp += 1;
+ if (rgb_comp > 2)
+ rgb_comp = 0;
+ }
+ else
+ {
+ int f = 0;
+ if (parms.format == SANE_FRAME_RED)
+ f = 1;
+ if (parms.format == SANE_FRAME_GREEN)
+ f = 2;
+ if (parms.format == SANE_FRAME_BLUE)
+ f = 3;
+ if (f)
+ {
+ uc = *((unsigned char *) (data + x));
+ uc = (unsigned char) gamma[f][(int) uc];
+ *((unsigned char *) data + x) = uc;
+ }
+ }
+ }
+ }
+ }
+ }
+ *length = len;
+ DBG (2, "sane_read: read %d bytes\n", len);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (2, "sane_cancel: handle = %p\n", handle);
+ pass = 0;
+ if (infile != NULL)
+ {
+ fclose (infile);
+ infile = NULL;
+ }
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
+ non_blocking);
+ if (!infile)
+ {
+ DBG (1, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
+ fd ? "!=" : "=");
+ return SANE_STATUS_UNSUPPORTED;
+}