diff options
Diffstat (limited to 'backend/test.c')
-rw-r--r-- | backend/test.c | 2841 |
1 files changed, 2841 insertions, 0 deletions
diff --git a/backend/test.c b/backend/test.c new file mode 100644 index 0000000..8f8851a --- /dev/null +++ b/backend/test.c @@ -0,0 +1,2841 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2002-2006 Henning Meier-Geinitz <henning@meier-geinitz.de> + Changes according to the sanei_thread usage by + Gerhard Jaeger <gerhard@gjaeger.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 28 + +#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 <time.h> +#include <unistd.h> +#include <fcntl.h> + +#include "../include/_stdint.h" + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_thread.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 (-42.17), + SANE_FIX (32767.9999), + SANE_FIX (2.0) +}; + +static SANE_String_Const mode_list[] = { + SANE_VALUE_SCAN_MODE_GRAY, + SANE_VALUE_SCAN_MODE_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 pattern"), 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[] = { + SANE_I18N ("First entry"), SANE_I18N ("Second entry"), + SANE_I18N + ("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[] = { + SANE_I18N ("First entry"), SANE_I18N ("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, -8, 17, 42, 42 +}; + +static SANE_String_Const source_list[] = { + SANE_I18N ("Flatbed"), SANE_I18N ("Automatic Document Feeder"), + 0 +}; + +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 =SANE_VALUE_SCAN_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_scan_source = "Flatbed"; +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; + uint8_t *firstbyte = (uint8_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", (void *) 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; + + test_device->loaded[opt_num_opts] = 1; + + /* 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, SANE_VALUE_SCAN_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, SANE_VALUE_SCAN_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_scan_source */ + od = &test_device->opt[opt_scan_source]; + od->name = SANE_NAME_SCAN_SOURCE; + od->title = SANE_TITLE_SCAN_SOURCE; + od->desc = SANE_I18N("If Automatic Document Feeder is selected, the feeder will be 'empty' after 10 scans."); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (source_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = source_list; + test_device->val[opt_scan_source].s = malloc (od->size); + if (!test_device->val[opt_scan_source].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_scan_source].s, init_scan_source); + + /* 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_endianness */ + od = &test_device->opt[opt_invert_endianess]; + od->name = "invert-endianess"; + od->title = SANE_I18N ("Invert endianness"); + 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 endianness."); + 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 transferred 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 = SANE_CAP_ADVANCED; + 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 = SANE_CAP_ADVANCED; + 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 dpi 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 with 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 = SANE_CAP_ADVANCED; + 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 (41.83); + + /* 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 (!word) + return SANE_STATUS_INVAL; + + 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 (!word) + return SANE_STATUS_INVAL; + 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); + if (!word) + return SANE_STATUS_INVAL; + 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); + if (!word) + return SANE_STATUS_INVAL; + 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 (!word) + return SANE_STATUS_INVAL; + 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", + (void *) 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=%lu\n", + buffer, (u_long) 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 %ld bytes of %lu (%d total)\n", + (long) bytes_written, (u_long) write_count, byte_count); + write_count -= bytes_written; + } + + free (buffer); + + if (sanei_thread_is_forked ()) + { + DBG (4, "(child) reader_process: finished, wrote %d bytes, expected %d " + "bytes, now waiting\n", byte_count, bytes_total); + while (SANE_TRUE) + sleep (10); + DBG (4, "(child) reader_process: this should have never happened..."); + close (fd); + } + else + { + DBG (4, "(child) reader_process: finished, wrote %d bytes, expected %d " + "bytes\n", byte_count, bytes_total); + } + return SANE_STATUS_GOOD; +} + +/* + * this code either runs in child or thread context... + */ +static int +reader_task (void *data) +{ + SANE_Status status; + struct SIGACTION act; + struct Test_Device *test_device = (struct Test_Device *) data; + + DBG (2, "reader_task started\n"); + if (sanei_thread_is_forked ()) + { + DBG (3, "reader_task started (forked)\n"); + close (test_device->pipe); + test_device->pipe = -1; + + } + else + { + DBG (3, "reader_task started (as thread)\n"); + } + + memset (&act, 0, sizeof (act)); + sigaction (SIGTERM, &act, 0); + + status = reader_process (test_device, test_device->reader_fds); + DBG (2, "(child) reader_task: reader_process finished (%s)\n", + sane_strstatus (status)); + return (int) status; +} + +static SANE_Status +finish_pass (Test_Device * test_device) +{ + SANE_Status return_status = SANE_STATUS_GOOD; + + DBG (2, "finish_pass: test_device=%p\n", (void *) test_device); + test_device->scanning = SANE_FALSE; + if (test_device->pipe >= 0) + { + DBG (2, "finish_pass: closing pipe\n"); + close (test_device->pipe); + DBG (2, "finish_pass: pipe closed\n"); + test_device->pipe = -1; + } + if (test_device->reader_pid != -1) + { + int status; + SANE_Pid pid; + + DBG (2, "finish_pass: terminating reader process %ld\n", + (long) test_device->reader_pid); + sanei_thread_kill (test_device->reader_pid); + pid = sanei_thread_waitpid (test_device->reader_pid, &status); + if (pid == -1) + { + DBG (1, + "finish_pass: sanei_thread_waitpid failed, already terminated? (%s)\n", + strerror (errno)); + } + else + { + DBG (2, "finish_pass: reader process terminated with status: %s\n", + sane_strstatus (status)); + } + test_device->reader_pid = -1; + } + /* this happens when running in thread context... */ + if (test_device->reader_fds >= 0) + { + DBG (2, "finish_pass: closing reader pipe\n"); + close (test_device->reader_fds); + DBG (2, "finish_pass: reader pipe closed\n"); + test_device->reader_fds = -1; + } + 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 * __sane_unused__ version_code, SANE_Auth_Callback __sane_unused__ 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 (); + sanei_thread_init (); + + test_device = 0; + previous_device = 0; + + DBG (1, "sane_init: SANE test 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); + + 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, "scan-source", param_string, + &init_scan_source) == 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 = -1; + test_device->pipe = -1; + 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", + (void *) 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, (void *) 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, + (void *) 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; + test_device->number_of_scans = 0; + + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + Test_Device *test_device = handle; + + DBG (2, "sane_close: handle=%p\n", (void *) 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", (void *) handle); + return; + } + if (!test_device->open) + { + DBG (1, "sane_close: handle %p not open\n", (void *) 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", + (void *) 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", + (void *) 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; + } + + test_device->loaded[option] = 1; + + 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", + (void *) handle, option, action, (void *) value, (void *) 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", (void *) 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 (!test_device->loaded[option]) + { + DBG (1, "sane_control_option: option not loaded\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: + case opt_scan_source: + 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, SANE_VALUE_SCAN_MODE_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: + case opt_scan_source: + 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; + + if(myinfo & SANE_INFO_RELOAD_OPTIONS){ + SANE_Int i = 0; + for(i=1;i<num_options;i++){ + test_device->loaded[i] = 0; + } + } + + 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", + (void *) handle, (void *) 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", (void *) handle); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_get_parameters: handle %p not open\n", (void *) 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, SANE_VALUE_SCAN_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 * (int) ((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 (3, "sane_get_parameters: format=%s\n", text_format); + DBG (3, "sane_get_parameters: last_frame=%s\n", + p->last_frame ? "true" : "false"); + DBG (3, "sane_get_parameters: lines=%d\n", p->lines); + DBG (3, "sane_get_parameters: depth=%d\n", p->depth); + DBG (3, "sane_get_parameters: pixels_per_line=%d\n", p->pixels_per_line); + DBG (3, "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, SANE_VALUE_SCAN_MODE_COLOR) == 0)) + { + DBG (1, "sane_start: already scanning\n"); + return SANE_STATUS_INVAL; + } + if (strcmp (test_device->val[opt_mode].s, SANE_VALUE_SCAN_MODE_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; + } + + if (test_device->pass == 0) + { + test_device->number_of_scans++; + DBG (3, "sane_start: scanning page %d\n", test_device->number_of_scans); + + if ((strcmp (test_device->val[opt_scan_source].s, "Automatic Document Feeder") == 0) && + (((test_device->number_of_scans) % 11) == 0)) + { + DBG (1, "sane_start: Document feeder is out of documents!\n"); + return SANE_STATUS_NO_DOCS; + } + } + + 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; + } + + /* create reader routine as new process or thread */ + test_device->pipe = pipe_descriptor[0]; + test_device->reader_fds = pipe_descriptor[1]; + test_device->reader_pid = + sanei_thread_begin (reader_task, (void *) test_device); + + if (test_device->reader_pid == -1) + { + DBG (1, "sane_start: sanei_thread_begin failed (%s)\n", + strerror (errno)); + return SANE_STATUS_NO_MEM; + } + + if (sanei_thread_is_forked ()) + { + close (test_device->reader_fds); + test_device->reader_fds = -1; + } + + 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; + SANE_Int bytes_total = test_device->lines * test_device->bytes_per_line; + + + DBG (4, "sane_read: handle=%p, data=%p, max_length = %d, length=%p\n", + handle, data, max_length, (void *) 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->eof) + { + DBG (2, "sane_read: No more data available, sending EOF\n"); + return SANE_STATUS_EOF; + } + if (!test_device->scanning) + { + DBG (1, "sane_read: not scanning (call sane_start first)\n"); + return SANE_STATUS_INVAL; + } + read_count = max_scan_length; + + bytes_read = read (test_device->pipe, data, read_count); + if (bytes_read == 0 + || (bytes_read + test_device->bytes_total >= bytes_total)) + { + 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, SANE_VALUE_SCAN_MODE_COLOR) == 0 + && test_device->val[opt_three_pass].w == SANE_TRUE) + { + test_device->pass++; + if (test_device->pass > 2) + test_device->pass = 0; + } + if (bytes_read == 0) + return SANE_STATUS_EOF; + } + else if (bytes_read < 0) + { + if (errno == EAGAIN) + { + DBG (2, "sane_read: no data available, try again\n"); + return SANE_STATUS_GOOD; + } + else + { + DBG (1, "sane_read: read 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 %ld bytes of %d, total %d\n", (long) bytes_read, + max_scan_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_cancel: scan already cancelled\n"); + return; + } + if (!test_device->scanning) + { + DBG (2, "sane_cancel: scan is already finished\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; +} |