sane-project-backends/backend/test.c

2724 wiersze
86 KiB
C

/* sane - Scanner Access Now Easy.
Copyright (C) 2002 Henning Meier-Geinitz <henning@meier-geinitz.de>
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 backend is for testing frontends.
*/
#define BUILD 17
#include "../include/sane/config.h"
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei_config.h"
#define BACKEND_NAME test
#include "../include/sane/sanei_backend.h"
#include "test.h"
#include "test-picture.c"
#define TEST_CONFIG_FILE "test.conf"
static SANE_Bool inited = SANE_FALSE;
static SANE_Device **sane_device_list = 0;
static Test_Device *first_test_device = 0;
static SANE_Range geometry_range = {
SANE_FIX (0.0),
SANE_FIX (200.0),
SANE_FIX (1.0)
};
static SANE_Range resolution_range = {
SANE_FIX (1.0),
SANE_FIX (1200.0),
SANE_FIX (1.0)
};
static SANE_Range ppl_loss_range = {
0,
128,
1
};
static SANE_Range read_limit_size_range = {
1,
64 * 1024, /* 64 KB ought to be enough for everyone :-) */
1
};
static SANE_Range read_delay_duration_range = {
1000,
200 * 1000, /* 200 msec */
1000
};
static SANE_Range int_constraint_range = {
4,
192,
2
};
static SANE_Range fixed_constraint_range = {
SANE_FIX (17001.0),
SANE_FIX (32767.0),
SANE_FIX (2.0)
};
static SANE_String_Const mode_list[] = {
SANE_I18N ("Gray"), SANE_I18N ("Color"),
0
};
static SANE_String_Const order_list[] = {
"RGB", "RBG", "GBR", "GRB", "BRG", "BGR",
0
};
static SANE_String_Const test_picture_list[] = {
SANE_I18N ("Solid black"), SANE_I18N ("Solid white"),
SANE_I18N ("Color patterns"), SANE_I18N ("Grid"),
0
};
static SANE_String_Const read_status_code_list[] = {
SANE_I18N ("Default"), "SANE_STATUS_UNSUPPORTED", "SANE_STATUS_CANCELLED",
"SANE_STATUS_DEVICE_BUSY", "SANE_STATUS_INVAL", "SANE_STATUS_EOF",
"SANE_STATUS_JAMMED", "SANE_STATUS_NO_DOCS", "SANE_STATUS_COVER_OPEN",
"SANE_STATUS_IO_ERROR", "SANE_STATUS_NO_MEM", "SANE_STATUS_ACCESS_DENIED",
0
};
static SANE_Int depth_list[] = {
3, 1, 8, 16
};
static SANE_Int int_constraint_word_list[] = {
9, -42, -8, 0, 17, 42, 256, 65536, 256 * 256 * 256, 256 * 256 * 256 * 64
};
static SANE_Int fixed_constraint_word_list[] = {
4, SANE_FIX (-32.7), SANE_FIX (12.1), SANE_FIX (42.0), SANE_FIX (129.5)
};
static SANE_String_Const string_constraint_string_list[] = {
"First entry", "Second entry",
"This is the very long third entry. Maybe the frontend has an idea how to "
"display it",
0
};
static SANE_String_Const string_constraint_long_string_list[] = {
"First entry", "Second entry", "3", "4", "5", "6", "7", "8", "9", "10",
"11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22",
"23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34",
"35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46",
0
};
static SANE_Int int_array[] = {
-17, 0, -5, 42, 91, 256 * 256 * 256 * 64
};
static SANE_Int int_array_constraint_range[] = {
48, 6, 4, 92, 190, 16
};
static SANE_Int int_array_constraint_word_list[] = {
-42, 0, 9, 17, 42, 42
};
static double random_factor; /* use for fuzzyness of parameters */
/* initial values */
static SANE_Word init_number_of_devices = 2;
static SANE_Fixed init_tl_x = SANE_FIX (0.0);
static SANE_Fixed init_tl_y = SANE_FIX (0.0);
static SANE_Fixed init_br_x = SANE_FIX (80.0);
static SANE_Fixed init_br_y = SANE_FIX (100.0);
static SANE_Word init_resolution = 50;
static SANE_String init_mode = "Gray";
static SANE_Word init_depth = 8;
static SANE_Bool init_hand_scanner = SANE_FALSE;
static SANE_Bool init_three_pass = SANE_FALSE;
static SANE_String init_three_pass_order = "RGB";
static SANE_String init_test_picture = "Solid black";
static SANE_Bool init_invert_endianess = SANE_FALSE;
static SANE_Bool init_read_limit = SANE_FALSE;
static SANE_Word init_read_limit_size = 1;
static SANE_Bool init_read_delay = SANE_FALSE;
static SANE_Word init_read_delay_duration = 1000;
static SANE_String init_read_status_code = "Default";
static SANE_Bool init_fuzzy_parameters = SANE_FALSE;
static SANE_Word init_ppl_loss = 0;
static SANE_Bool init_non_blocking = SANE_FALSE;
static SANE_Bool init_select_fd = SANE_FALSE;
static SANE_Bool init_enable_test_options = SANE_FALSE;
static SANE_String init_string = "This is the contents of the string option. "
"Fill some more words to see how the frontend behaves.";
static SANE_String init_string_constraint_string_list = "First entry";
static SANE_String init_string_constraint_long_string_list = "First entry";
/* Test if this machine is little endian (from coolscan.c) */
static SANE_Bool
little_endian (void)
{
SANE_Int testvalue = 255;
u_int8_t *firstbyte = (u_int8_t *) & testvalue;
if (*firstbyte == 255)
return SANE_TRUE;
return SANE_FALSE;
}
static void
swap_double (double *a, double *b)
{
double c;
c = *a;
*a = *b;
*b = c;
return;
}
static size_t
max_string_size (const SANE_String_Const strings[])
{
size_t size, max_size = 0;
SANE_Int i;
for (i = 0; strings[i]; ++i)
{
size = strlen (strings[i]) + 1;
if (size > max_size)
max_size = size;
}
return max_size;
}
static SANE_Bool
check_handle (SANE_Handle handle)
{
Test_Device *test_device = first_test_device;
while (test_device)
{
if (test_device == (Test_Device *) handle)
return SANE_TRUE;
test_device = test_device->next;
}
return SANE_FALSE;
}
static SANE_Status
init_options (Test_Device * test_device)
{
SANE_Option_Descriptor *od;
DBG (2, "init_options: test_device=%p\n", test_device);
/* opt_num_opts */
od = &test_device->opt[opt_num_opts];
od->name = "";
od->title = SANE_TITLE_NUM_OPTIONS;
od->desc = SANE_DESC_NUM_OPTIONS;
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_num_opts].w = num_options;
/* opt_mode_group */
od = &test_device->opt[opt_mode_group];
od->name = "";
od->title = SANE_I18N ("Scan Mode");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_mode_group].w = 0;
/* opt_mode */
od = &test_device->opt[opt_mode];
od->name = SANE_NAME_SCAN_MODE;
od->title = SANE_TITLE_SCAN_MODE;
od->desc = SANE_DESC_SCAN_MODE;
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = max_string_size (mode_list);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
od->constraint.string_list = mode_list;
test_device->val[opt_mode].s = malloc (od->size);
if (!test_device->val[opt_mode].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_mode].s, init_mode);
/* opt_depth */
od = &test_device->opt[opt_depth];
od->name = SANE_NAME_BIT_DEPTH;
od->title = SANE_TITLE_BIT_DEPTH;
od->desc = SANE_DESC_BIT_DEPTH;
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
od->constraint.word_list = depth_list;
test_device->val[opt_depth].w = init_depth;
/* opt_hand_scanner */
od = &test_device->opt[opt_hand_scanner];
od->name = "hand-scanner";
od->title = SANE_I18N ("Hand-scanner simulation");
od->desc = SANE_I18N ("Simulate a hand-scanner. Hand-scanners 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. This option also enables a fixed width "
"of 11 cm.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_hand_scanner].w = init_hand_scanner;
/* opt_three_pass */
od = &test_device->opt[opt_three_pass];
od->name = "three-pass";
od->title = SANE_I18N ("Three-pass simulation");
od->desc = SANE_I18N ("Simulate a three-pass scanner. In color mode, three "
"frames are transmitted.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (strcmp (init_mode, "Color") != 0)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_three_pass].w = init_three_pass;
/* opt_three_pass_order */
od = &test_device->opt[opt_three_pass_order];
od->name = "three-pass-order";
od->title = SANE_I18N ("Set the order of frames.");
od->desc = SANE_I18N ("Set the order of frames in three-pass color mode.");
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = max_string_size (order_list);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (strcmp (init_mode, "Color") != 0)
od->cap |= SANE_CAP_INACTIVE;
if (!init_three_pass)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
od->constraint.string_list = order_list;
test_device->val[opt_three_pass_order].s = malloc (od->size);
if (!test_device->val[opt_three_pass_order].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_three_pass_order].s, init_three_pass_order);
/* opt_resolution */
od = &test_device->opt[opt_resolution];
od->name = SANE_NAME_SCAN_RESOLUTION;
od->title = SANE_TITLE_SCAN_RESOLUTION;
od->desc = SANE_DESC_SCAN_RESOLUTION;
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_DPI;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &resolution_range;
test_device->val[opt_resolution].w = init_resolution;
/* opt_special_group */
od = &test_device->opt[opt_special_group];
od->name = "";
od->title = SANE_I18N ("Special Options");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_special_group].w = 0;
/* opt_test_picture */
od = &test_device->opt[opt_test_picture];
od->name = "test-picture";
od->title = SANE_I18N ("Select the test picture");
od->desc =
SANE_I18N ("Select the kind of test picture. Available options:\n"
"Solid black: fills the whole scan with black.\n"
"Solid white: fills the whole scan with white.\n"
"Color pattern: draws various color test patterns "
"depending on the mode.\n"
"Grid: draws a black/white grid with a width and "
"height of 10 mm per square.");
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = max_string_size (test_picture_list);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
od->constraint.string_list = test_picture_list;
test_device->val[opt_test_picture].s = malloc (od->size);
if (!test_device->val[opt_test_picture].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_test_picture].s, init_test_picture);
/* opt_invert_endianess */
od = &test_device->opt[opt_invert_endianess];
od->name = "invert-endianess";
od->title = SANE_I18N ("Invert endianess");
od->desc = SANE_I18N ("Exchange upper and lower byte of image data in 16 "
"bit modes. This option can be used to test the 16 "
"bit modes of frontends, e.g. if the frontend uses "
"the correct endianess.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_invert_endianess].w = init_invert_endianess;
/* opt_read_limit */
od = &test_device->opt[opt_read_limit];
od->name = "read-limit";
od->title = SANE_I18N ("Read limit");
od->desc = SANE_I18N ("Limit the amount of data tranferred with each "
"call to sane_read().");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_read_limit].w = init_read_limit;
/* opt_read_limit_size */
od = &test_device->opt[opt_read_limit_size];
od->name = "read-limit-size";
od->title = SANE_I18N ("Size of read-limit");
od->desc = SANE_I18N ("The (maximum) amount of data transferred with each "
"call to sane_read().");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (!init_read_limit)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &read_limit_size_range;
test_device->val[opt_read_limit_size].w = init_read_limit_size;
/* opt_read_delay */
od = &test_device->opt[opt_read_delay];
od->name = "read-delay";
od->title = SANE_I18N ("Read delay");
od->desc = SANE_I18N ("Delay the transfer of data to the pipe.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_read_delay].w = init_read_delay;
/* opt_read_delay_duration */
od = &test_device->opt[opt_read_delay_duration];
od->name = "read-delay-duration";
od->title = SANE_I18N ("Duration of read-delay");
od->desc = SANE_I18N ("How long to wait after transferring each buffer of "
"data through the pipe.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_MICROSECOND;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (!init_read_delay)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &read_delay_duration_range;
test_device->val[opt_read_delay_duration].w = init_read_delay_duration;
/* opt_read_status_code */
od = &test_device->opt[opt_read_status_code];
od->name = "read-return-value";
od->title = SANE_I18N ("Return-value of sane_read");
od->desc =
SANE_I18N ("Select the return-value of sane_read(). \"Default\" "
"is the normal handling for scanning. All other status "
"codes are for testing how the frontend handles them.");
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = max_string_size (read_status_code_list);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
od->constraint.string_list = read_status_code_list;
test_device->val[opt_read_status_code].s = malloc (od->size);
if (!test_device->val[opt_read_status_code].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_read_status_code].s, init_read_status_code);
/* opt_ppl_loss */
od = &test_device->opt[opt_ppl_loss];
od->name = "ppl-loss";
od->title = SANE_I18N ("Loss of pixels per line");
od->desc =
SANE_I18N ("The number of pixels that are wasted at the end of each "
"line.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_PIXEL;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &ppl_loss_range;
test_device->val[opt_ppl_loss].w = init_ppl_loss;
/* opt_fuzzy_parameters */
od = &test_device->opt[opt_fuzzy_parameters];
od->name = "fuzzy-parameters";
od->title = SANE_I18N ("Fuzzy parameters");
od->desc = SANE_I18N ("Return fuzzy lines and bytes per line when "
"sane_parameters() is called before sane_start().");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_fuzzy_parameters].w = init_fuzzy_parameters;
/* opt_non_blocking */
od = &test_device->opt[opt_non_blocking];
od->name = "non-blocking";
od->title = SANE_I18N ("Use non-blocking IO");
od->desc = SANE_I18N ("Use non-blocking IO for sane_read() if supported "
"by the frontend.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_non_blocking].w = init_non_blocking;
/* opt_select_fd */
od = &test_device->opt[opt_select_fd];
od->name = "select-fd";
od->title = SANE_I18N ("Offer select file descriptor");
od->desc = SANE_I18N ("Offer a select filedescriptor for detecting if "
"sane_read() will return data.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_select_fd].w = init_select_fd;
/* opt_enable_test_options */
od = &test_device->opt[opt_enable_test_options];
od->name = "enable-test-options";
od->title = SANE_I18N ("Enable test options");
od->desc = SANE_I18N ("Enable various test options. This is for testing "
"the ability of frontends to view and modify all the "
"different SANE option types.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_enable_test_options].w = init_enable_test_options;
/* opt_print_options */
od = &test_device->opt[opt_print_options];
od->name = "print-options";
od->title = SANE_I18N ("Print options");
od->desc = SANE_I18N ("Print a list of all options.");
od->type = SANE_TYPE_BUTTON;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.string_list = 0;
test_device->val[opt_print_options].w = 0;
/* opt_geometry_group */
od = &test_device->opt[opt_geometry_group];
od->name = "";
od->title = SANE_I18N ("Geometry");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_geometry_group].w = 0;
/* opt_tl_x */
od = &test_device->opt[opt_tl_x];
od->name = SANE_NAME_SCAN_TL_X;
od->title = SANE_TITLE_SCAN_TL_X;
od->desc = SANE_DESC_SCAN_TL_X;
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_MM;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &geometry_range;
test_device->val[opt_tl_x].w = init_tl_x;
/* opt_tl_y */
od = &test_device->opt[opt_tl_y];
od->name = SANE_NAME_SCAN_TL_Y;
od->title = SANE_TITLE_SCAN_TL_Y;
od->desc = SANE_DESC_SCAN_TL_Y;
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_MM;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &geometry_range;
test_device->val[opt_tl_y].w = init_tl_y;
/* opt_br_x */
od = &test_device->opt[opt_br_x];
od->name = SANE_NAME_SCAN_BR_X;
od->title = SANE_TITLE_SCAN_BR_X;
od->desc = SANE_DESC_SCAN_BR_X;
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_MM;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &geometry_range;
test_device->val[opt_br_x].w = init_br_x;
/* opt_br_y */
od = &test_device->opt[opt_br_y];
od->name = SANE_NAME_SCAN_BR_Y;
od->title = SANE_TITLE_SCAN_BR_Y;
od->desc = SANE_DESC_SCAN_BR_Y;
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_MM;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &geometry_range;
test_device->val[opt_br_y].w = init_br_y;
/* opt_bool_group */
od = &test_device->opt[opt_bool_group];
od->name = "";
od->title = SANE_I18N ("Bool test options");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_group].w = 0;
/* opt_bool_soft_select_soft_detect */
od = &test_device->opt[opt_bool_soft_select_soft_detect];
od->name = "bool-soft-select-soft-detect";
od->title = SANE_I18N ("(1/6) Bool soft select soft detect");
od->desc =
SANE_I18N ("(1/6) Bool test option that has soft select and soft "
"detect (and advanced) capabilities. That's just a "
"normal bool option.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_soft_select_soft_detect].w = SANE_FALSE;
/* opt_bool_hard_select_soft_detect */
od = &test_device->opt[opt_bool_hard_select_soft_detect];
od->name = "bool-hard-select-soft-detect";
od->title = SANE_I18N ("(2/6) Bool hard select soft detect");
od->desc =
SANE_I18N ("(2/6) Bool test option that has hard select and soft "
"detect (and advanced) capabilities. That means the "
"option can't be set by the frontend but by the user "
"(e.g. by pressing a button at the device).");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_hard_select_soft_detect].w = SANE_FALSE;
/* opt_bool_hard_select */
od = &test_device->opt[opt_bool_hard_select];
od->name = "bool-hard-select";
od->title = SANE_I18N ("(3/6) Bool hard select");
od->desc = SANE_I18N ("(3/6) Bool test option that has hard select "
"(and advanced) capabilities. That means the option "
"can't be set by the frontend but by the user "
"(e.g. by pressing a button at the device) and can't "
"be read by the frontend.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_hard_select].w = SANE_FALSE;
/* opt_bool_soft_detect */
od = &test_device->opt[opt_bool_soft_detect];
od->name = "bool-soft-detect";
od->title = SANE_I18N ("(4/6) Bool soft detect");
od->desc = SANE_I18N ("(4/6) Bool test option that has soft detect "
"(and advanced) capabilities. That means the option "
"is read-only.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_soft_detect].w = SANE_FALSE;
/* opt_bool_soft_select_soft_detect_emulated */
od = &test_device->opt[opt_bool_soft_select_soft_detect_emulated];
od->name = "bool-soft-select-soft-detect-emulated";
od->title = SANE_I18N ("(5/6) Bool soft select soft detect emulated");
od->desc = SANE_I18N ("(5/6) Bool test option that has soft select, soft "
"detect, and emulated (and advanced) capabilities. ");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED
| SANE_CAP_EMULATED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_soft_select_soft_detect_emulated].w = SANE_FALSE;
/* opt_bool_soft_select_soft_detect_auto */
od = &test_device->opt[opt_bool_soft_select_soft_detect_auto];
od->name = "bool-soft-select-soft-detect-auto";
od->title = SANE_I18N ("(6/6) Bool soft select soft detect auto");
od->desc = SANE_I18N ("(6/6) Bool test option that has soft select, soft "
"detect, and automatic (and advanced) capabilities. "
"This option can be automatically set by the backend.");
od->type = SANE_TYPE_BOOL;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED
| SANE_CAP_AUTOMATIC;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_bool_soft_select_soft_detect_auto].w = SANE_FALSE;
/* opt_int_group */
od = &test_device->opt[opt_int_group];
od->name = "";
od->title = SANE_I18N ("Int test options");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_int_group].w = 0;
/* opt_int */
od = &test_device->opt[opt_int];
od->name = "int";
od->title = SANE_I18N ("(1/6) Int");
od->desc = SANE_I18N ("(1/6) Int test option with no unit and no "
"constraint set.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_int].w = 42;
/* opt_int_constraint_range */
od = &test_device->opt[opt_int_constraint_range];
od->name = "int-constraint-range";
od->title = SANE_I18N ("(2/6) Int constraint range");
od->desc = SANE_I18N ("(2/6) Int test option with unit pixel and "
"constraint range set. Minimum is 4, maximum 192, and "
"quant is 2.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_PIXEL;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &int_constraint_range;
test_device->val[opt_int_constraint_range].w = 26;
/* opt_int_constraint_word_list */
od = &test_device->opt[opt_int_constraint_word_list];
od->name = "int-constraint-word-list";
od->title = SANE_I18N ("(3/6) Int constraint word list");
od->desc = SANE_I18N ("(3/6) Int test option with unit bits and "
"constraint word list set. ");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_BIT;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
od->constraint.word_list = int_constraint_word_list;
test_device->val[opt_int_constraint_word_list].w = 42;
/* opt_int_array */
od = &test_device->opt[opt_int_array];
od->name = "int-constraint-array";
od->title = SANE_I18N ("(4/6) Int array");
od->desc = SANE_I18N ("(4/6) Int test option with unit mm and using "
"an array without constraints.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_MM;
od->size = 6 * sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_int_array].wa = &int_array[0];
/* opt_int_array_constraint_range */
od = &test_device->opt[opt_int_array_constraint_range];
od->name = "int-constraint-array-constraint-range";
od->title = SANE_I18N ("(5/6) Int array constraint range");
od->desc = SANE_I18N ("(5/6) Int test option with unit mm and using "
"an array with a range constraint. Minimum is 4, "
"maximum 192, and quant is 2.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_DPI;
od->size = 6 * sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &int_constraint_range;
test_device->val[opt_int_array_constraint_range].wa =
&int_array_constraint_range[0];
/* opt_int_array_constraint_word_list */
od = &test_device->opt[opt_int_array_constraint_word_list];
od->name = "int-constraint-array-constraint-word-list";
od->title = SANE_I18N ("(6/6) Int array constraint word list");
od->desc = SANE_I18N ("(6/6) Int test option with unit percent and using "
"an array a word list constraint.");
od->type = SANE_TYPE_INT;
od->unit = SANE_UNIT_PERCENT;
od->size = 6 * sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
od->constraint.word_list = int_constraint_word_list;
test_device->val[opt_int_array_constraint_word_list].wa =
&int_array_constraint_word_list[0];
/* opt_fixed_group */
od = &test_device->opt[opt_fixed_group];
od->name = "";
od->title = SANE_I18N ("Fixed test options");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_fixed_group].w = 0;
/* opt_fixed */
od = &test_device->opt[opt_fixed];
od->name = "fixed";
od->title = SANE_I18N ("(1/3) Fixed");
od->desc = SANE_I18N ("(1/3) Fixed test option with no unit and no "
"constraint set.");
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_fixed].w = SANE_FIX (42.0);
/* opt_fixed_constraint_range */
od = &test_device->opt[opt_fixed_constraint_range];
od->name = "fixed-constraint-range";
od->title = SANE_I18N ("(2/3) Fixed constraint range");
od->desc = SANE_I18N ("(2/3) Fixed test option with unit microsecond and "
"constraint range set. Minimum is -42.17, maximum "
"32767.9999, and quant is 2.0.");
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_MICROSECOND;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_RANGE;
od->constraint.range = &fixed_constraint_range;
test_device->val[opt_fixed_constraint_range].w = SANE_FIX (42.0);
/* opt_fixed_constraint_word_list */
od = &test_device->opt[opt_fixed_constraint_word_list];
od->name = "fixed-constraint-word-list";
od->title = SANE_I18N ("(3/3) Fixed constraint word list");
od->desc = SANE_I18N ("(3/3) Fixed test option with no unit and "
"constraint word list set. ");
od->type = SANE_TYPE_FIXED;
od->unit = SANE_UNIT_NONE;
od->size = sizeof (SANE_Word);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
od->constraint.word_list = fixed_constraint_word_list;
test_device->val[opt_fixed_constraint_word_list].w = SANE_FIX (42.0);
/* opt_string_group */
od = &test_device->opt[opt_string_group];
od->name = "";
od->title = SANE_I18N ("String test options");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_string_group].w = 0;
/* opt_string */
od = &test_device->opt[opt_string];
od->name = "string";
od->title = SANE_I18N ("(1/3) String");
od->desc = SANE_I18N ("(1/3) String test option without constraint.");
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = strlen (init_string) + 1;
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.string_list = 0;
test_device->val[opt_string].s = malloc (od->size);
if (!test_device->val[opt_string].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_string].s, init_string);
/* opt_string_constraint_string_list */
od = &test_device->opt[opt_string_constraint_string_list];
od->name = "string-constraint-string-list";
od->title = SANE_I18N ("(2/3) String constraint string list");
od->desc = SANE_I18N ("(2/3) String test option with string list "
"constraint.");
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = max_string_size (string_constraint_string_list);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
od->constraint.string_list = string_constraint_string_list;
test_device->val[opt_string_constraint_string_list].s = malloc (od->size);
if (!test_device->val[opt_string_constraint_string_list].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_string_constraint_string_list].s,
init_string_constraint_string_list);
/* opt_string_constraint_long_string_list */
od = &test_device->opt[opt_string_constraint_long_string_list];
od->name = "string-constraint-long-string-list";
od->title = SANE_I18N ("(3/3) String constraint long string list");
od->desc = SANE_I18N ("(3/3) String test option with string list "
"constraint. Contains some more entries...");
od->type = SANE_TYPE_STRING;
od->unit = SANE_UNIT_NONE;
od->size = max_string_size (string_constraint_long_string_list);
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
od->constraint.string_list = string_constraint_long_string_list;
test_device->val[opt_string_constraint_long_string_list].s =
malloc (od->size);
if (!test_device->val[opt_string_constraint_long_string_list].s)
return SANE_STATUS_NO_MEM;
strcpy (test_device->val[opt_string_constraint_long_string_list].s,
init_string_constraint_long_string_list);
/* opt_button_group */
od = &test_device->opt[opt_button_group];
od->name = "";
od->title = SANE_I18N ("Button test options");
od->desc = "";
od->type = SANE_TYPE_GROUP;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = 0;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.range = 0;
test_device->val[opt_button_group].w = 0;
/* opt_button */
od = &test_device->opt[opt_button];
od->name = "button";
od->title = SANE_I18N ("(1/1) Button");
od->desc = SANE_I18N ("(1/1) Button test option. Prints some text...");
od->type = SANE_TYPE_BUTTON;
od->unit = SANE_UNIT_NONE;
od->size = 0;
od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
if (init_enable_test_options == SANE_FALSE)
od->cap |= SANE_CAP_INACTIVE;
od->constraint_type = SANE_CONSTRAINT_NONE;
od->constraint.string_list = 0;
test_device->val[opt_button].w = 0;
return SANE_STATUS_GOOD;
}
static SANE_Status
read_option (SANE_String line, SANE_String option_string,
parameter_type p_type, void *value)
{
SANE_String_Const cp;
SANE_Char *word, *end;
word = 0;
cp = sanei_config_get_string (line, &word);
if (strcmp (word, option_string) != 0)
return SANE_STATUS_INVAL;
free (word);
word = 0;
switch (p_type)
{
case param_none:
return SANE_STATUS_GOOD;
case param_bool:
{
cp = sanei_config_get_string (cp, &word);
if (strlen (word) == 0)
{
DBG (3, "read_option: option `%s' requires parameter\n",
option_string);
return SANE_STATUS_INVAL;
}
if (strcmp (word, "true") != 0 && strcmp (word, "false") != 0)
{
DBG (3, "read_option: option `%s' requires parameter "
"`true' or `false'\n", option_string);
return SANE_STATUS_INVAL;
}
else if (strcmp (word, "true") == 0)
*(SANE_Bool *) value = SANE_TRUE;
else
*(SANE_Bool *) value = SANE_FALSE;
DBG (3, "read_option: set option `%s' to %s\n", option_string,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
}
case param_int:
{
SANE_Int int_value;
cp = sanei_config_get_string (cp, &word);
errno = 0;
int_value = (SANE_Int) strtol (word, &end, 0);
if (end == word)
{
DBG (3, "read_option: option `%s' requires parameter\n",
option_string);
return SANE_STATUS_INVAL;
}
else if (errno)
{
DBG (3, "read_option: option `%s': can't parse parameter `%s' "
"(%s)\n", option_string, word, strerror (errno));
return SANE_STATUS_INVAL;
}
else
{
DBG (3, "read_option: set option `%s' to %d\n", option_string,
int_value);
*(SANE_Int *) value = int_value;
}
break;
}
case param_fixed:
{
double double_value;
SANE_Fixed fixed_value;
cp = sanei_config_get_string (cp, &word);
errno = 0;
double_value = strtod (word, &end);
if (end == word)
{
DBG (3, "read_option: option `%s' requires parameter\n",
option_string);
return SANE_STATUS_INVAL;
}
else if (errno)
{
DBG (3, "read_option: option `%s': can't parse parameter `%s' "
"(%s)\n", option_string, word, strerror (errno));
return SANE_STATUS_INVAL;
}
else
{
DBG (3, "read_option: set option `%s' to %.0f\n", option_string,
double_value);
fixed_value = SANE_FIX (double_value);
*(SANE_Fixed *) value = fixed_value;
}
break;
}
case param_string:
{
cp = sanei_config_get_string (cp, &word);
if (strlen (word) == 0)
{
DBG (3, "read_option: option `%s' requires parameter\n",
option_string);
return SANE_STATUS_INVAL;
}
else
{
DBG (3, "read_option: set option `%s' to `%s'\n", option_string,
word);
*(SANE_String *) value = strdup (word);
}
break;
}
default:
DBG (1, "read_option: unknown param_type %d\n", p_type);
return SANE_STATUS_INVAL;
} /* switch */
if (word)
free (word);
word = 0;
return SANE_STATUS_GOOD;
}
static SANE_Status
reader_process (Test_Device * test_device, SANE_Int fd)
{
SANE_Status status;
SANE_Word byte_count = 0, bytes_total;
SANE_Byte *buffer = 0;
ssize_t bytes_written = 0;
size_t buffer_size = 0, write_count = 0;
DBG (2, "(child) reader_process: test_device=%p, fd=%d\n", test_device, fd);
bytes_total = test_device->lines * test_device->bytes_per_line;
status = init_picture_buffer (test_device, &buffer, &buffer_size);
if (status != SANE_STATUS_GOOD)
return status;
DBG (2, "(child) reader_process: buffer=%p, buffersize=%d\n",
buffer, buffer_size);
while (byte_count < bytes_total)
{
if (write_count == 0)
{
write_count = buffer_size;
if (byte_count + (SANE_Word) write_count > bytes_total)
write_count = bytes_total - byte_count;
if (test_device->val[opt_read_delay].w == SANE_TRUE)
usleep (test_device->val[opt_read_delay_duration].w);
}
bytes_written = write (fd, buffer, write_count);
if (bytes_written < 0)
{
DBG (1, "(child) reader_process: write returned %s\n",
strerror (errno));
return SANE_STATUS_IO_ERROR;
}
byte_count += bytes_written;
DBG (4, "(child) reader_process: wrote %d bytes of %d (%d total)\n",
bytes_written, write_count, byte_count);
write_count -= bytes_written;
}
free (buffer);
DBG (4, "(child) reader_process: finished, wrote %d bytes, expected %d "
"bytes\n", byte_count, bytes_total);
return SANE_STATUS_GOOD;
}
static SANE_Status
finish_pass (Test_Device * test_device)
{
SANE_Status return_status = SANE_STATUS_GOOD;
DBG (2, "finish_pass: test_device=%p\n", test_device);
if (test_device->reader_pid > 0)
{
int status;
int pid;
DBG (2, "finish_pass: terminating reader process %d\n",
test_device->reader_pid);
kill (test_device->reader_pid, SIGTERM);
pid = waitpid (test_device->reader_pid, &status, 0);
if (pid < 0)
{
DBG (1, "finish_pass: waitpid failed, already terminated? (%s)\n",
strerror (errno));
}
else if (WIFEXITED (status))
{
DBG (2, "finish_pass: reader process terminated with status %s\n",
sane_strstatus (WEXITSTATUS (status)));
if (WEXITSTATUS (status) != SANE_STATUS_GOOD)
return_status = WEXITSTATUS (status);
}
else if (WIFSIGNALED (status))
{
DBG (2, "finish_pass: reader process was terminated by signal %d\n",
WTERMSIG (status));
if (WTERMSIG (status) != 15)
return_status = SANE_STATUS_IO_ERROR;
}
else
DBG (1, "finish_pass: reader process terminated by unknown reason\n");
test_device->reader_pid = 0;
}
if (test_device->pipe > 0)
{
DBG (2, "finish_pass: closing pipe\n");
fclose (test_device->pipe_handle);
close (test_device->pipe);
test_device->pipe = 0;
}
return return_status;
}
static void
print_options (Test_Device * test_device)
{
SANE_Option_Descriptor *od;
SANE_Word option_number;
SANE_Char caps[1024];
for (option_number = 0; option_number < num_options; option_number++)
{
od = &test_device->opt[option_number];
DBG (0, "-----> number: %d\n", option_number);
DBG (0, " name: `%s'\n", od->name);
DBG (0, " title: `%s'\n", od->title);
DBG (0, " description: `%s'\n", od->desc);
DBG (0, " type: %s\n",
od->type == SANE_TYPE_BOOL ? "SANE_TYPE_BOOL" :
od->type == SANE_TYPE_INT ? "SANE_TYPE_INT" :
od->type == SANE_TYPE_FIXED ? "SANE_TYPE_FIXED" :
od->type == SANE_TYPE_STRING ? "SANE_TYPE_STRING" :
od->type == SANE_TYPE_BUTTON ? "SANE_TYPE_BUTTON" :
od->type == SANE_TYPE_GROUP ? "SANE_TYPE_GROUP" : "unknown");
DBG (0, " unit: %s\n",
od->unit == SANE_UNIT_NONE ? "SANE_UNIT_NONE" :
od->unit == SANE_UNIT_PIXEL ? "SANE_UNIT_PIXEL" :
od->unit == SANE_UNIT_BIT ? "SANE_UNIT_BIT" :
od->unit == SANE_UNIT_MM ? "SANE_UNIT_MM" :
od->unit == SANE_UNIT_DPI ? "SANE_UNIT_DPI" :
od->unit == SANE_UNIT_PERCENT ? "SANE_UNIT_PERCENT" :
od->unit == SANE_UNIT_MICROSECOND ? "SANE_UNIT_MICROSECOND" :
"unknown");
DBG (0, " size: %d\n", od->size);
caps[0] = '\0';
if (od->cap & SANE_CAP_SOFT_SELECT)
strcat (caps, "SANE_CAP_SOFT_SELECT ");
if (od->cap & SANE_CAP_HARD_SELECT)
strcat (caps, "SANE_CAP_HARD_SELECT ");
if (od->cap & SANE_CAP_SOFT_DETECT)
strcat (caps, "SANE_CAP_SOFT_DETECT ");
if (od->cap & SANE_CAP_EMULATED)
strcat (caps, "SANE_CAP_EMULATED ");
if (od->cap & SANE_CAP_AUTOMATIC)
strcat (caps, "SANE_CAP_AUTOMATIC ");
if (od->cap & SANE_CAP_INACTIVE)
strcat (caps, "SANE_CAP_INACTIVE ");
if (od->cap & SANE_CAP_ADVANCED)
strcat (caps, "SANE_CAP_ADVANCED ");
DBG (0, " capabilities: %s\n", caps);
DBG (0, "constraint type: %s\n",
od->constraint_type == SANE_CONSTRAINT_NONE ?
"SANE_CONSTRAINT_NONE" :
od->constraint_type == SANE_CONSTRAINT_RANGE ?
"SANE_CONSTRAINT_RANGE" :
od->constraint_type == SANE_CONSTRAINT_WORD_LIST ?
"SANE_CONSTRAINT_WORD_LIST" :
od->constraint_type == SANE_CONSTRAINT_STRING_LIST ?
"SANE_CONSTRAINT_STRING_LIST" : "unknown");
}
}
/***************************** SANE API ****************************/
SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
FILE *fp;
SANE_Int linenumber;
SANE_Char line[PATH_MAX], *word;
SANE_String_Const cp;
SANE_Device *sane_device;
Test_Device *test_device, *previous_device;
SANE_Int num;
DBG_INIT ();
test_device = 0;
previous_device = 0;
DBG (2, "sane_init: version_code= %p, authorize=%p\n", version_code,
authorize);
DBG (1, "sane_init: SANE test backend version %d.%d.%d from %s\n",
V_MAJOR, V_MINOR, BUILD, PACKAGE_VERSION);
if (version_code)
*version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
if (inited)
DBG (3, "sane_init: warning: already inited\n");
fp = sanei_config_open (TEST_CONFIG_FILE);
if (fp)
{
linenumber = 0;
DBG (4, "sane_init: reading config file `%s'\n", TEST_CONFIG_FILE);
while (sanei_config_read (line, sizeof (line), fp))
{
word = 0;
linenumber++;
cp = sanei_config_get_string (line, &word);
if (!word || cp == line)
{
DBG (5,
"sane_init: config file line %3d: ignoring empty line\n",
linenumber);
if (word)
free (word);
continue;
}
if (word[0] == '#')
{
DBG (5,
"sane_init: config file line %3d: ignoring comment line\n",
linenumber);
free (word);
continue;
}
DBG (5, "sane_init: config file line %3d: `%s'\n",
linenumber, line);
if (read_option (line, "number_of_devices", param_int,
&init_number_of_devices) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "mode", param_string,
&init_mode) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "hand-scanner", param_bool,
&init_hand_scanner) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "three-pass", param_bool,
&init_three_pass) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "three-pass-order", param_string,
&init_three_pass_order) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "resolution_min", param_fixed,
&resolution_range.min) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "resolution_max", param_fixed,
&resolution_range.max) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "resolution_quant", param_fixed,
&resolution_range.quant) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "resolution", param_fixed,
&init_resolution) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "depth", param_int,
&init_depth) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "test-picture", param_string,
&init_test_picture) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "invert-endianess", param_bool,
&init_invert_endianess) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "read-limit", param_bool,
&init_read_limit) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "read-limit-size", param_int,
&init_read_limit_size) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "read-delay", param_bool,
&init_read_delay) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "read-delay-duration", param_int,
&init_read_delay_duration) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "read-status-code", param_string,
&init_read_status_code) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "ppl-loss", param_int,
&init_ppl_loss) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "fuzzy-parameters", param_bool,
&init_fuzzy_parameters) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "non-blocking", param_bool,
&init_non_blocking) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "select-fd", param_bool,
&init_select_fd) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "enable-test-options", param_bool,
&init_enable_test_options) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "geometry_min", param_fixed,
&geometry_range.min) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "geometry_max", param_fixed,
&geometry_range.max) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "geometry_quant", param_fixed,
&geometry_range.quant) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "tl_x", param_fixed,
&init_tl_x) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "tl_y", param_fixed,
&init_tl_y) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "br_x", param_fixed,
&init_br_x) == SANE_STATUS_GOOD)
continue;
if (read_option (line, "br_y", param_fixed,
&init_br_y) == SANE_STATUS_GOOD)
continue;
DBG (3, "sane-init: I don't know how to handle option `%s'\n",
word);
} /* while */
fclose (fp);
} /* if */
else
{
DBG (3, "sane_init: couldn't find config file (%s), using default "
"settings\n", TEST_CONFIG_FILE);
}
/* create devices */
sane_device_list =
malloc ((init_number_of_devices + 1) * sizeof (sane_device));
if (!sane_device_list)
return SANE_STATUS_NO_MEM;
for (num = 0; num < init_number_of_devices; num++)
{
SANE_Char number_string[PATH_MAX];
test_device = malloc (sizeof (*test_device));
if (!test_device)
return SANE_STATUS_NO_MEM;
test_device->sane.vendor = "Noname";
test_device->sane.type = "virtual device";
test_device->sane.model = "frontend-tester";
snprintf (number_string, sizeof (number_string), "%d", num);
number_string[sizeof (number_string) - 1] = '\0';
test_device->name = strdup (number_string);
if (!test_device->name)
return SANE_STATUS_NO_MEM;
test_device->sane.name = test_device->name;
if (previous_device)
previous_device->next = test_device;
previous_device = test_device;
if (num == 0)
first_test_device = test_device;
sane_device_list[num] = &test_device->sane;
test_device->open = SANE_FALSE;
test_device->eof = SANE_FALSE;
test_device->scanning = SANE_FALSE;
test_device->cancelled = SANE_FALSE;
test_device->reader_pid = 0;
test_device->pipe = 0;
DBG (4, "sane_init: new device: `%s' is a %s %s %s\n",
test_device->sane.name, test_device->sane.vendor,
test_device->sane.model, test_device->sane.type);
}
test_device->next = 0;
sane_device_list[num] = 0;
srand (time (NULL));
random_factor = ((double) rand ()) / RAND_MAX + 0.5;
inited = SANE_TRUE;
return SANE_STATUS_GOOD;
}
void
sane_exit (void)
{
Test_Device *test_device, *previous_device;
DBG (2, "sane_exit\n");
if (!inited)
{
DBG (1, "sane_exit: not inited, call sane_init() first\n");
return;
}
test_device = first_test_device;
while (test_device)
{
DBG (4, "sane_exit: freeing device %s\n", test_device->name);
previous_device = test_device;
test_device = test_device->next;
if (previous_device->name)
free (previous_device->name);
free (previous_device);
}
DBG (4, "sane_exit: freeing device list\n");
if (sane_device_list)
free (sane_device_list);
sane_device_list = NULL;
first_test_device = NULL;
inited = SANE_FALSE;
return;
}
SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
DBG (2, "sane_get_devices: device_list=%p, local_only=%d\n",
device_list, local_only);
if (!inited)
{
DBG (1, "sane_get_devices: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!device_list)
{
DBG (1, "sane_get_devices: device_list == 0\n");
return SANE_STATUS_INVAL;
}
*device_list = (const SANE_Device **) sane_device_list;
return SANE_STATUS_GOOD;
}
SANE_Status
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
{
Test_Device *test_device = first_test_device;
SANE_Status status;
DBG (2, "sane_open: devicename = \"%s\", handle=%p\n", devicename, handle);
if (!inited)
{
DBG (1, "sane_open: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!handle)
{
DBG (1, "sane_open: handle == 0\n");
return SANE_STATUS_INVAL;
}
if (!devicename || strlen (devicename) == 0)
{
DBG (2, "sane_open: device name NULL or empty\n");
}
else
{
for (test_device = first_test_device; test_device;
test_device = test_device->next)
{
if (strcmp (devicename, test_device->name) == 0)
break;
}
}
if (!test_device)
{
DBG (1, "sane_open: device `%s' not found\n", devicename);
return SANE_STATUS_INVAL;
}
if (test_device->open)
{
DBG (1, "sane_open: device `%s' already open\n", devicename);
return SANE_STATUS_DEVICE_BUSY;
}
DBG (2, "sane_open: opening device `%s', handle = %p\n", test_device->name,
test_device);
test_device->open = SANE_TRUE;
*handle = test_device;
status = init_options (test_device);
if (status != SANE_STATUS_GOOD)
return status;
test_device->open = SANE_TRUE;
test_device->scanning = SANE_FALSE;
test_device->cancelled = SANE_FALSE;
test_device->eof = SANE_FALSE;
test_device->bytes_total = 0;
test_device->pass = 0;
return SANE_STATUS_GOOD;
}
void
sane_close (SANE_Handle handle)
{
Test_Device *test_device = handle;
DBG (2, "sane_close: handle=%p\n", handle);
if (!inited)
{
DBG (1, "sane_close: not inited, call sane_init() first\n");
return;
}
if (!check_handle (handle))
{
DBG (1, "sane_close: handle %p unknown\n", handle);
return;
}
if (!test_device->open)
{
DBG (1, "sane_close: handle %p not open\n", handle);
return;
}
test_device->open = SANE_FALSE;
return;
}
const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
{
Test_Device *test_device = handle;
DBG (4, "sane_get_option_descriptor: handle=%p, option = %d\n", handle,
option);
if (!inited)
{
DBG (1, "sane_get_option_descriptor: not inited, call sane_init() "
"first\n");
return 0;
}
if (!check_handle (handle))
{
DBG (1, "sane_get_option_descriptor: handle %p unknown\n", handle);
return 0;
}
if (!test_device->open)
{
DBG (1, "sane_get_option_descriptor: not open\n");
return 0;
}
if (option < 0 || option >= num_options)
{
DBG (3, "sane_get_option_descriptor: option < 0 || "
"option > num_options\n");
return 0;
}
return &test_device->opt[option];
}
SANE_Status
sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
void *value, SANE_Int * info)
{
Test_Device *test_device = handle;
SANE_Int myinfo = 0;
SANE_Status status;
DBG (4, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
handle, option, action, value, info);
if (!inited)
{
DBG (1, "sane_control_option: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!check_handle (handle))
{
DBG (1, "sane_control_option: handle %p unknown\n", handle);
return SANE_STATUS_INVAL;
}
if (!test_device->open)
{
DBG (1, "sane_control_option: not open\n");
return SANE_STATUS_INVAL;
}
if (test_device->scanning)
{
DBG (1, "sane_control_option: is scanning\n");
return SANE_STATUS_INVAL;
}
if (option < 0 || option >= num_options)
{
DBG (1, "sane_control_option: option < 0 || option > num_options\n");
return SANE_STATUS_INVAL;
}
if (!SANE_OPTION_IS_ACTIVE (test_device->opt[option].cap))
{
DBG (1, "sane_control_option: option is inactive\n");
return SANE_STATUS_INVAL;
}
if (test_device->opt[option].type == SANE_TYPE_GROUP)
{
DBG (1, "sane_control_option: option is a group\n");
return SANE_STATUS_INVAL;
}
switch (action)
{
case SANE_ACTION_SET_AUTO:
if (!SANE_OPTION_IS_SETTABLE (test_device->opt[option].cap))
{
DBG (1, "sane_control_option: option is not setable\n");
return SANE_STATUS_INVAL;
}
if (!(test_device->opt[option].cap & SANE_CAP_AUTOMATIC))
{
DBG (1, "sane_control_option: option is not automatically "
"setable\n");
return SANE_STATUS_INVAL;
}
switch (option)
{
case opt_bool_soft_select_soft_detect_auto:
test_device->val[option].w = SANE_TRUE;
DBG (4, "sane_control_option: set option %d (%s) automatically "
"to %s\n", option, test_device->opt[option].name,
test_device->val[option].w == SANE_TRUE ? "true" : "false");
break;
default:
DBG (1, "sane_control_option: trying to automatically set "
"unexpected option\n");
return SANE_STATUS_INVAL;
}
break;
case SANE_ACTION_SET_VALUE:
if (!SANE_OPTION_IS_SETTABLE (test_device->opt[option].cap))
{
DBG (1, "sane_control_option: option is not setable\n");
return SANE_STATUS_INVAL;
}
status = sanei_constrain_value (&test_device->opt[option],
value, &myinfo);
if (status != SANE_STATUS_GOOD)
{
DBG (3, "sane_control_option: sanei_constrain_value returned %s\n",
sane_strstatus (status));
return status;
}
switch (option)
{
case opt_tl_x: /* Fixed with parameter reloading */
case opt_tl_y:
case opt_br_x:
case opt_br_y:
case opt_resolution:
if (test_device->val[option].w == *(SANE_Fixed *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Fixed *) value;
myinfo |= SANE_INFO_RELOAD_PARAMS;
DBG (4, "sane_control_option: set option %d (%s) to %.0f %s\n",
option, test_device->opt[option].name,
SANE_UNFIX (*(SANE_Fixed *) value),
test_device->opt[option].unit == SANE_UNIT_MM ? "mm" : "dpi");
break;
case opt_fixed: /* Fixed */
case opt_fixed_constraint_range:
if (test_device->val[option].w == *(SANE_Fixed *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Fixed *) value;
DBG (4, "sane_control_option: set option %d (%s) to %.0f\n",
option, test_device->opt[option].name,
SANE_UNFIX (*(SANE_Fixed *) value));
break;
case opt_read_limit_size: /* Int */
case opt_ppl_loss:
case opt_read_delay_duration:
case opt_int:
case opt_int_constraint_range:
if (test_device->val[option].w == *(SANE_Int *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Int *) value;
DBG (4, "sane_control_option: set option %d (%s) to %d\n",
option, test_device->opt[option].name, *(SANE_Int *) value);
break;
case opt_fuzzy_parameters: /* Bool with parameter reloading */
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
myinfo |= SANE_INFO_RELOAD_PARAMS;
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_invert_endianess: /* Bool */
case opt_non_blocking:
case opt_select_fd:
case opt_bool_soft_select_soft_detect:
case opt_bool_soft_select_soft_detect_auto:
case opt_bool_soft_select_soft_detect_emulated:
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_depth: /* Word list with parameter and options reloading */
if (test_device->val[option].w == *(SANE_Int *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Int *) value;
if (test_device->val[option].w == 16)
test_device->opt[opt_invert_endianess].cap &= ~ SANE_CAP_INACTIVE;
else
test_device->opt[opt_invert_endianess].cap |= SANE_CAP_INACTIVE;
myinfo |= SANE_INFO_RELOAD_PARAMS;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
DBG (4, "sane_control_option: set option %d (%s) to %d\n",
option, test_device->opt[option].name, *(SANE_Int *) value);
break;
case opt_three_pass_order: /* String list with parameter reload */
if (strcmp (test_device->val[option].s, value) == 0)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
strcpy (test_device->val[option].s, (SANE_String) value);
myinfo |= SANE_INFO_RELOAD_PARAMS;
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name, (SANE_String) value);
break;
case opt_int_constraint_word_list: /* Word list */
case opt_fixed_constraint_word_list:
if (test_device->val[option].w == *(SANE_Int *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Int *) value;
DBG (4, "sane_control_option: set option %d (%s) to %d\n",
option, test_device->opt[option].name, *(SANE_Int *) value);
break;
case opt_read_status_code: /* String (list) */
case opt_test_picture:
case opt_string:
case opt_string_constraint_string_list:
case opt_string_constraint_long_string_list:
if (strcmp (test_device->val[option].s, value) == 0)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
strcpy (test_device->val[option].s, (SANE_String) value);
DBG (4, "sane_control_option: set option %d (%s) to `%s'\n",
option, test_device->opt[option].name, (SANE_String) value);
break;
case opt_int_array: /* Word array */
case opt_int_array_constraint_range:
case opt_int_array_constraint_word_list:
memcpy (test_device->val[option].wa, value,
test_device->opt[option].size);
DBG (4, "sane_control_option: set option %d (%s) to %p\n",
option, test_device->opt[option].name, (void *) value);
break;
/* options with side-effects */
case opt_print_options:
DBG (4, "sane_control_option: set option %d (%s)\n",
option, test_device->opt[option].name);
print_options (test_device);
break;
case opt_button:
DBG (0, "Yes! You pressed me!\n");
DBG (4, "sane_control_option: set option %d (%s)\n",
option, test_device->opt[option].name);
break;
case opt_mode:
if (strcmp (test_device->val[option].s, value) == 0)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
strcpy (test_device->val[option].s, (SANE_String) value);
myinfo |= SANE_INFO_RELOAD_PARAMS;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
if (strcmp (test_device->val[option].s, "Color") == 0)
{
test_device->opt[opt_three_pass].cap &= ~SANE_CAP_INACTIVE;
if (test_device->val[opt_three_pass].w == SANE_TRUE)
test_device->opt[opt_three_pass_order].cap
&= ~SANE_CAP_INACTIVE;
}
else
{
test_device->opt[opt_three_pass].cap |= SANE_CAP_INACTIVE;
test_device->opt[opt_three_pass_order].cap |= SANE_CAP_INACTIVE;
}
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name, (SANE_String) value);
break;
case opt_three_pass:
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
myinfo |= SANE_INFO_RELOAD_PARAMS;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
if (test_device->val[option].w == SANE_TRUE)
test_device->opt[opt_three_pass_order].cap &= ~SANE_CAP_INACTIVE;
else
test_device->opt[opt_three_pass_order].cap |= SANE_CAP_INACTIVE;
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_hand_scanner:
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
myinfo |= SANE_INFO_RELOAD_PARAMS;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
if (test_device->val[option].w == SANE_TRUE)
{
test_device->opt[opt_tl_x].cap |= SANE_CAP_INACTIVE;
test_device->opt[opt_tl_y].cap |= SANE_CAP_INACTIVE;
test_device->opt[opt_br_x].cap |= SANE_CAP_INACTIVE;
test_device->opt[opt_br_y].cap |= SANE_CAP_INACTIVE;
}
else
{
test_device->opt[opt_tl_x].cap &= ~SANE_CAP_INACTIVE;
test_device->opt[opt_tl_y].cap &= ~SANE_CAP_INACTIVE;
test_device->opt[opt_br_x].cap &= ~SANE_CAP_INACTIVE;
test_device->opt[opt_br_y].cap &= ~SANE_CAP_INACTIVE;
}
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_read_limit:
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
if (test_device->val[option].w == SANE_TRUE)
test_device->opt[opt_read_limit_size].cap &= ~SANE_CAP_INACTIVE;
else
test_device->opt[opt_read_limit_size].cap |= SANE_CAP_INACTIVE;
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_read_delay:
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
if (test_device->val[option].w == SANE_TRUE)
test_device->opt[opt_read_delay_duration].cap
&= ~SANE_CAP_INACTIVE;
else
test_device->opt[opt_read_delay_duration].cap |=
SANE_CAP_INACTIVE;
DBG (4, "sane_control_option: set option %d (%s) to %s\n", option,
test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_enable_test_options:
{
int option_number;
if (test_device->val[option].w == *(SANE_Bool *) value)
{
DBG (4, "sane_control_option: option %d (%s) not changed\n",
option, test_device->opt[option].name);
break;
}
test_device->val[option].w = *(SANE_Bool *) value;
myinfo |= SANE_INFO_RELOAD_OPTIONS;
for (option_number = opt_bool_soft_select_soft_detect;
option_number < num_options; option_number++)
{
if (test_device->opt[option_number].type == SANE_TYPE_GROUP)
continue;
if (test_device->val[option].w == SANE_TRUE)
test_device->opt[option_number].cap &= ~SANE_CAP_INACTIVE;
else
test_device->opt[option_number].cap |= SANE_CAP_INACTIVE;
}
DBG (4, "sane_control_option: set option %d (%s) to %s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
}
default:
DBG (1, "sane_control_option: trying to set unexpected option\n");
return SANE_STATUS_INVAL;
}
break;
case SANE_ACTION_GET_VALUE:
switch (option)
{
case opt_num_opts:
*(SANE_Word *) value = num_options;
DBG (4, "sane_control_option: get option 0, value = %d\n",
num_options);
break;
case opt_tl_x: /* Fixed options */
case opt_tl_y:
case opt_br_x:
case opt_br_y:
case opt_resolution:
case opt_fixed:
case opt_fixed_constraint_range:
case opt_fixed_constraint_word_list:
{
*(SANE_Fixed *) value = test_device->val[option].w;
DBG (4,
"sane_control_option: get option %d (%s), value=%.1f %s\n",
option, test_device->opt[option].name,
SANE_UNFIX (*(SANE_Fixed *) value),
test_device->opt[option].unit ==
SANE_UNIT_MM ? "mm" : SANE_UNIT_DPI ? "dpi" : "");
break;
}
case opt_hand_scanner: /* Bool options */
case opt_three_pass:
case opt_invert_endianess:
case opt_read_limit:
case opt_read_delay:
case opt_fuzzy_parameters:
case opt_non_blocking:
case opt_select_fd:
case opt_bool_soft_select_soft_detect:
case opt_bool_hard_select_soft_detect:
case opt_bool_soft_detect:
case opt_enable_test_options:
case opt_bool_soft_select_soft_detect_emulated:
case opt_bool_soft_select_soft_detect_auto:
*(SANE_Bool *) value = test_device->val[option].w;
DBG (4,
"sane_control_option: get option %d (%s), value=%s\n",
option, test_device->opt[option].name,
*(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
break;
case opt_mode: /* String (list) options */
case opt_three_pass_order:
case opt_read_status_code:
case opt_test_picture:
case opt_string:
case opt_string_constraint_string_list:
case opt_string_constraint_long_string_list:
strcpy (value, test_device->val[option].s);
DBG (4, "sane_control_option: get option %d (%s), value=`%s'\n",
option, test_device->opt[option].name, (SANE_String) value);
break;
case opt_depth: /* Int + word list options */
case opt_read_limit_size:
case opt_ppl_loss:
case opt_read_delay_duration:
case opt_int:
case opt_int_constraint_range:
case opt_int_constraint_word_list:
*(SANE_Int *) value = test_device->val[option].w;
DBG (4, "sane_control_option: get option %d (%s), value=%d\n",
option, test_device->opt[option].name, *(SANE_Int *) value);
break;
case opt_int_array: /* Int array */
case opt_int_array_constraint_range:
case opt_int_array_constraint_word_list:
memcpy (value, test_device->val[option].wa,
test_device->opt[option].size);
DBG (4, "sane_control_option: get option %d (%s), value=%p\n",
option, test_device->opt[option].name, (void *) value);
break;
default:
DBG (1, "sane_control_option: trying to get unexpected option\n");
return SANE_STATUS_INVAL;
}
break;
default:
DBG (1, "sane_control_option: trying unexpected action %d\n", action);
return SANE_STATUS_INVAL;
}
if (info)
*info = myinfo;
DBG (4, "sane_control_option: finished, info=%s %s %s \n",
myinfo & SANE_INFO_INEXACT ? "inexact" : "",
myinfo & SANE_INFO_RELOAD_PARAMS ? "reload_parameters" : "",
myinfo & SANE_INFO_RELOAD_OPTIONS ? "reload_options" : "");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
{
Test_Device *test_device = handle;
SANE_Parameters *p;
double res, tl_x = 0, tl_y = 0, br_x = 0, br_y = 0;
SANE_String text_format, mode;
SANE_Int channels = 1;
DBG (2, "sane_get_parameters: handle=%p, params=%p\n", handle, params);
if (!inited)
{
DBG (1, "sane_get_parameters: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!check_handle (handle))
{
DBG (1, "sane_get_parameters: handle %p unknown\n", handle);
return SANE_STATUS_INVAL;
}
if (!test_device->open)
{
DBG (1, "sane_get_parameters: handle %p not open\n", handle);
return SANE_STATUS_INVAL;
}
res = SANE_UNFIX (test_device->val[opt_resolution].w);
mode = test_device->val[opt_mode].s;
p = &test_device->params;
p->depth = test_device->val[opt_depth].w;
if (test_device->val[opt_hand_scanner].w == SANE_TRUE)
{
tl_x = 0.0;
br_x = 110.0;
tl_y = 0.0;
br_y = 170.0;
p->lines = -1;
test_device->lines = (SANE_Word) (res * (br_y - tl_y) / MM_PER_INCH);
}
else
{
tl_x = SANE_UNFIX (test_device->val[opt_tl_x].w);
tl_y = SANE_UNFIX (test_device->val[opt_tl_y].w);
br_x = SANE_UNFIX (test_device->val[opt_br_x].w);
br_y = SANE_UNFIX (test_device->val[opt_br_y].w);
if (tl_x > br_x)
swap_double (&tl_x, &br_x);
if (tl_y > br_y)
swap_double (&tl_y, &br_y);
test_device->lines = (SANE_Word) (res * (br_y - tl_y) / MM_PER_INCH);
if (test_device->lines < 1)
test_device->lines = 1;
p->lines = test_device->lines;
if (test_device->val[opt_fuzzy_parameters].w == SANE_TRUE
&& test_device->scanning == SANE_FALSE)
p->lines *= random_factor;
}
if (strcmp (mode, "Gray") == 0)
{
p->format = SANE_FRAME_GRAY;
p->last_frame = SANE_TRUE;
}
else /* Color */
{
if (test_device->val[opt_three_pass].w == SANE_TRUE)
{
if (test_device->val[opt_three_pass_order].s[test_device->pass]
== 'R')
p->format = SANE_FRAME_RED;
else if (test_device->val[opt_three_pass_order].s[test_device->pass]
== 'G')
p->format = SANE_FRAME_GREEN;
else
p->format = SANE_FRAME_BLUE;
if (test_device->pass > 1)
p->last_frame = SANE_TRUE;
else
p->last_frame = SANE_FALSE;
}
else
{
p->format = SANE_FRAME_RGB;
p->last_frame = SANE_TRUE;
}
}
p->pixels_per_line = (SANE_Int) (res * (br_x - tl_x) / MM_PER_INCH);
if (test_device->val[opt_fuzzy_parameters].w == SANE_TRUE
&& test_device->scanning == SANE_FALSE)
p->pixels_per_line *= random_factor;
if (p->pixels_per_line < 1)
p->pixels_per_line = 1;
if (p->format == SANE_FRAME_RGB)
channels = 3;
if (p->depth == 1)
p->bytes_per_line = channels * (p->pixels_per_line + 7) / 8;
else /* depth == 8 || depth == 16 */
p->bytes_per_line = channels * p->pixels_per_line * ((p->depth + 7) / 8);
test_device->bytes_per_line = p->bytes_per_line;
p->pixels_per_line -= test_device->val[opt_ppl_loss].w;
if (p->pixels_per_line < 1)
p->pixels_per_line = 1;
test_device->pixels_per_line = p->pixels_per_line;
switch (p->format)
{
case SANE_FRAME_GRAY:
text_format = "gray";
break;
case SANE_FRAME_RGB:
text_format = "rgb";
break;
case SANE_FRAME_RED:
text_format = "red";
break;
case SANE_FRAME_GREEN:
text_format = "green";
break;
case SANE_FRAME_BLUE:
text_format = "blue";
break;
default:
text_format = "unknown";
break;
}
DBG (2, "sane_get_parameters: format=%s\n", text_format);
DBG (2, "sane_get_parameters: last_frame=%s\n",
p->last_frame ? "true" : "false");
DBG (2, "sane_get_parameters: lines=%d\n", p->lines);
DBG (2, "sane_get_parameters: depth=%d\n", p->depth);
DBG (2, "sane_get_parameters: pixels_per_line=%d\n", p->pixels_per_line);
DBG (2, "sane_get_parameters: bytes_per_line=%d\n", p->bytes_per_line);
if (params)
*params = *p;
return SANE_STATUS_GOOD;
}
SANE_Status
sane_start (SANE_Handle handle)
{
Test_Device *test_device = handle;
int pipe_descriptor[2];
DBG (2, "sane_start: handle=%p\n", handle);
if (!inited)
{
DBG (1, "sane_start: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!check_handle (handle))
{
DBG (1, "sane_start: handle %p unknown\n", handle);
return SANE_STATUS_INVAL;
}
if (!test_device->open)
{
DBG (1, "sane_start: not open\n");
return SANE_STATUS_INVAL;
}
if (test_device->scanning
&& (test_device->val[opt_three_pass].w == SANE_FALSE
&& strcmp (test_device->val[opt_mode].s, "Color") == 0))
{
DBG (1, "sane_start: already scanning\n");
return SANE_STATUS_INVAL;
}
if (strcmp (test_device->val[opt_mode].s, "Color") == 0
&& test_device->val[opt_three_pass].w == SANE_TRUE
&& test_device->pass > 2)
{
DBG (1, "sane_start: already in last pass of three\n");
return SANE_STATUS_INVAL;
}
test_device->scanning = SANE_TRUE;
test_device->cancelled = SANE_FALSE;
test_device->eof = SANE_FALSE;
test_device->bytes_total = 0;
sane_get_parameters (handle, 0);
if (test_device->params.lines == 0)
{
DBG (1, "sane_start: lines == 0\n");
test_device->scanning = SANE_FALSE;
return SANE_STATUS_INVAL;
}
if (test_device->params.pixels_per_line == 0)
{
DBG (1, "sane_start: pixels_per_line == 0\n");
test_device->scanning = SANE_FALSE;
return SANE_STATUS_INVAL;
}
if (test_device->params.bytes_per_line == 0)
{
DBG (1, "sane_start: bytes_per_line == 0\n");
test_device->scanning = SANE_FALSE;
return SANE_STATUS_INVAL;
}
if (pipe (pipe_descriptor) < 0)
{
DBG (1, "sane_start: pipe failed (%s)\n", strerror (errno));
return SANE_STATUS_IO_ERROR;
}
test_device->reader_pid = fork ();
if (test_device->reader_pid == 0) /* child */
{
SANE_Status status;
sigset_t ignore_set;
struct SIGACTION act;
/* block all signals but SIGTERM */
sigfillset (&ignore_set);
sigdelset (&ignore_set, SIGTERM);
sigprocmask (SIG_SETMASK, &ignore_set, 0);
memset (&act, 0, sizeof (act));
sigaction (SIGTERM, &act, 0);
close (pipe_descriptor[0]);
status = reader_process (test_device, pipe_descriptor[1]);
DBG (2, "(child) sane_start: reader_process returned %s\n",
sane_strstatus (status));
_exit (status);
}
else if (test_device->reader_pid < 0)
{
DBG (1, "sane_start: fork failed (%s)\n", strerror (errno));
return SANE_STATUS_NO_MEM;
}
/* parent */
close (pipe_descriptor[1]);
test_device->pipe = pipe_descriptor[0];
test_device->pipe_handle = fdopen (pipe_descriptor[0], "r");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * data,
SANE_Int max_length, SANE_Int * length)
{
Test_Device *test_device = handle;
SANE_Int max_scan_length;
ssize_t bytes_read;
size_t read_count;
DBG (4, "sane_read: handle=%p, data=%p, max_length = %d, length=%p\n",
handle, data, max_length, length);
if (!inited)
{
DBG (1, "sane_read: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!check_handle (handle))
{
DBG (1, "sane_read: handle %p unknown\n", handle);
return SANE_STATUS_INVAL;
}
if (!length)
{
DBG (1, "sane_read: length == NULL\n");
return SANE_STATUS_INVAL;
}
if (strcmp (test_device->val[opt_read_status_code].s, "Default") != 0)
{
SANE_String_Const sc = test_device->val[opt_read_status_code].s;
DBG (3, "sane_read: setting return status to %s\n", sc);
if (strcmp (sc, "SANE_STATUS_UNSUPPORTED") == 0)
return SANE_STATUS_UNSUPPORTED;
if (strcmp (sc, "SANE_STATUS_CANCELLED") == 0)
return SANE_STATUS_CANCELLED;
if (strcmp (sc, "SANE_STATUS_DEVICE_BUSY") == 0)
return SANE_STATUS_DEVICE_BUSY;
if (strcmp (sc, "SANE_STATUS_INVAL") == 0)
return SANE_STATUS_INVAL;
if (strcmp (sc, "SANE_STATUS_EOF") == 0)
return SANE_STATUS_EOF;
if (strcmp (sc, "SANE_STATUS_JAMMED") == 0)
return SANE_STATUS_JAMMED;
if (strcmp (sc, "SANE_STATUS_NO_DOCS") == 0)
return SANE_STATUS_NO_DOCS;
if (strcmp (sc, "SANE_STATUS_COVER_OPEN") == 0)
return SANE_STATUS_COVER_OPEN;
if (strcmp (sc, "SANE_STATUS_IO_ERROR") == 0)
return SANE_STATUS_IO_ERROR;
if (strcmp (sc, "SANE_STATUS_NO_MEM") == 0)
return SANE_STATUS_NO_MEM;
if (strcmp (sc, "SANE_STATUS_ACCESS_DENIED") == 0)
return SANE_STATUS_ACCESS_DENIED;
}
max_scan_length = max_length;
if (test_device->val[opt_read_limit].w == SANE_TRUE
&& test_device->val[opt_read_limit_size].w < max_scan_length)
{
max_scan_length = test_device->val[opt_read_limit_size].w;
DBG (3, "sane_read: limiting max_scan_length to %d bytes\n",
max_scan_length);
}
*length = 0;
if (!data)
{
DBG (1, "sane_read: data == NULL\n");
return SANE_STATUS_INVAL;
}
if (!test_device->open)
{
DBG (1, "sane_read: not open\n");
return SANE_STATUS_INVAL;
}
if (test_device->cancelled)
{
DBG (1, "sane_read: scan was cancelled\n");
return SANE_STATUS_CANCELLED;
}
if (!test_device->scanning)
{
DBG (1, "sane_read: not scanning (call sane_start first)\n");
return SANE_STATUS_INVAL;
}
if (test_device->eof)
{
DBG (2, "sane_read: EOF reached\n");
return SANE_STATUS_EOF;
}
read_count = max_scan_length;
bytes_read = fread (data, 1, read_count, test_device->pipe_handle);
if (feof (test_device->pipe_handle))
{
SANE_Status status;
DBG (2, "sane_read: EOF reached\n");
status = finish_pass (test_device);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "sane_read: finish_pass returned `%s'\n",
sane_strstatus (status));
return status;
}
test_device->eof = SANE_TRUE;
if (strcmp (test_device->val[opt_mode].s, "Color") == 0
&& test_device->val[opt_three_pass].w == SANE_TRUE)
test_device->pass++;
if (bytes_read == 0)
return SANE_STATUS_EOF;
}
else if (bytes_read == 0 && ferror (test_device->pipe_handle))
{
if (errno == EAGAIN)
{
DBG (2, "sane_read: no data available, try again\n");
return SANE_STATUS_GOOD;
}
else if (errno == 0)
{
DBG (2, "sane_read: huh? ferror set but errno == 0?\n");
clearerr (test_device->pipe_handle);
}
else
{
DBG (1, "sane_read: fread returned error: %s\n", strerror (errno));
return SANE_STATUS_IO_ERROR;
}
}
*length = bytes_read;
test_device->bytes_total += bytes_read;
DBG (2, "sane_read: read %d bytes of %d, total %d\n", bytes_read,
max_length, test_device->bytes_total);
return SANE_STATUS_GOOD;
}
void
sane_cancel (SANE_Handle handle)
{
Test_Device *test_device = handle;
DBG (2, "sane_cancel: handle = %p\n", handle);
if (!inited)
{
DBG (1, "sane_cancel: not inited, call sane_init() first\n");
return;
}
if (!check_handle (handle))
{
DBG (1, "sane_cancel: handle %p unknown\n", handle);
return;
}
if (!test_device->open)
{
DBG (1, "sane_cancel: not open\n");
return;
}
if (test_device->cancelled)
{
DBG (1, "sane_read: scan already cancelled\n");
return;
}
if (!test_device->scanning)
{
DBG (1, "sane_read: not scanning\n");
return;
}
finish_pass (test_device);
test_device->cancelled = SANE_TRUE;
test_device->scanning = SANE_FALSE;
test_device->eof = SANE_FALSE;
test_device->pass = 0;
return;
}
SANE_Status
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
{
Test_Device *test_device = handle;
DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
non_blocking);
if (!inited)
{
DBG (1, "sane_set_io_mode: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!check_handle (handle))
{
DBG (1, "sane_set_io_mode: handle %p unknown\n", handle);
return SANE_STATUS_INVAL;
}
if (!test_device->open)
{
DBG (1, "sane_set_io_mode: not open\n");
return SANE_STATUS_INVAL;
}
if (!test_device->scanning)
{
DBG (1, "sane_set_io_mode: not scanning\n");
return SANE_STATUS_INVAL;
}
if (test_device->val[opt_non_blocking].w == SANE_TRUE)
{
if (fcntl (test_device->pipe,
F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
{
DBG (1, "sane_set_io_mode: can't set io mode");
return SANE_STATUS_INVAL;
}
}
else
{
if (non_blocking)
return SANE_STATUS_UNSUPPORTED;
}
return SANE_STATUS_GOOD;
}
SANE_Status
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
{
Test_Device *test_device = handle;
DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
fd ? "!=" : "=");
if (!inited)
{
DBG (1, "sane_get_select_fd: not inited, call sane_init() first\n");
return SANE_STATUS_INVAL;
}
if (!check_handle (handle))
{
DBG (1, "sane_get_select_fd: handle %p unknown\n", handle);
return SANE_STATUS_INVAL;
}
if (!test_device->open)
{
DBG (1, "sane_get_select_fd: not open\n");
return SANE_STATUS_INVAL;
}
if (!test_device->scanning)
{
DBG (1, "sane_get_select_fd: not scanning\n");
return SANE_STATUS_INVAL;
}
if (test_device->val[opt_select_fd].w == SANE_TRUE)
{
*fd = test_device->pipe;
return SANE_STATUS_GOOD;
}
return SANE_STATUS_UNSUPPORTED;
}