1141cc406Sopenharmony_ci/*
2141cc406Sopenharmony_ci   tstbackend -- backend test utility
3141cc406Sopenharmony_ci
4141cc406Sopenharmony_ci   Uses the SANE library.
5141cc406Sopenharmony_ci   Copyright (C) 2002 Frank Zago (sane at zago dot net)
6141cc406Sopenharmony_ci   Copyright (C) 2013 Stéphane Voltz <stef.dev@free.fr> : sane_get_devices test
7141cc406Sopenharmony_ci
8141cc406Sopenharmony_ci   This file is part of the SANE package.
9141cc406Sopenharmony_ci
10141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
11141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
12141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
13141cc406Sopenharmony_ci   License, or (at your option) any later version.
14141cc406Sopenharmony_ci
15141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
16141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
17141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18141cc406Sopenharmony_ci   General Public License for more details.
19141cc406Sopenharmony_ci
20141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
21141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
22141cc406Sopenharmony_ci*/
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci#define BUILD 19				/* 2013-03-29 */
25141cc406Sopenharmony_ci
26141cc406Sopenharmony_ci#include "../include/sane/config.h"
27141cc406Sopenharmony_ci
28141cc406Sopenharmony_ci#include <assert.h>
29141cc406Sopenharmony_ci#include <getopt.h>
30141cc406Sopenharmony_ci#include <stdio.h>
31141cc406Sopenharmony_ci#include <stdlib.h>
32141cc406Sopenharmony_ci#include <string.h>
33141cc406Sopenharmony_ci#include <unistd.h>
34141cc406Sopenharmony_ci#include <stdarg.h>
35141cc406Sopenharmony_ci#include <time.h>
36141cc406Sopenharmony_ci
37141cc406Sopenharmony_ci#include <sys/types.h>
38141cc406Sopenharmony_ci#include <sys/stat.h>
39141cc406Sopenharmony_ci
40141cc406Sopenharmony_ci#include "../include/sane/sane.h"
41141cc406Sopenharmony_ci#include "../include/sane/sanei.h"
42141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
43141cc406Sopenharmony_ci
44141cc406Sopenharmony_cistatic struct option basic_options[] = {
45141cc406Sopenharmony_ci	{"device-name", required_argument, NULL, 'd'},
46141cc406Sopenharmony_ci	{"level", required_argument, NULL, 'l'},
47141cc406Sopenharmony_ci	{"scan", no_argument, NULL, 's'},
48141cc406Sopenharmony_ci	{"recursion", required_argument, NULL, 'r'},
49141cc406Sopenharmony_ci	{"get-devices", required_argument, NULL, 'g'},
50141cc406Sopenharmony_ci	{"help", no_argument, NULL, 'h'}
51141cc406Sopenharmony_ci};
52141cc406Sopenharmony_ci
53141cc406Sopenharmony_cistatic void
54141cc406Sopenharmony_citest_options (SANE_Device * device, int can_do_recursive);
55141cc406Sopenharmony_ci
56141cc406Sopenharmony_cienum message_level {
57141cc406Sopenharmony_ci	MSG,						/* info message */
58141cc406Sopenharmony_ci	INF,						/* non-urgent warning */
59141cc406Sopenharmony_ci	WRN,						/* warning */
60141cc406Sopenharmony_ci	ERR,						/* error, test can continue */
61141cc406Sopenharmony_ci	FATAL,						/* error, test can't/mustn't continue */
62141cc406Sopenharmony_ci	BUG							/* bug in tstbackend */
63141cc406Sopenharmony_ci};
64141cc406Sopenharmony_ci
65141cc406Sopenharmony_ciint message_number_wrn = 0;
66141cc406Sopenharmony_ciint message_number_err = 0;
67141cc406Sopenharmony_ci#ifdef HAVE_LONG_LONG
68141cc406Sopenharmony_cilong long checks_done = 0;
69141cc406Sopenharmony_ci#else
70141cc406Sopenharmony_ci/* It may overflow, but it's no big deal. */
71141cc406Sopenharmony_cilong int checks_done = 0;
72141cc406Sopenharmony_ci#endif
73141cc406Sopenharmony_ci
74141cc406Sopenharmony_ciint test_level;
75141cc406Sopenharmony_ciint verbose_level;
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_ci/* Maybe add that to sane.h */
78141cc406Sopenharmony_ci#define SANE_OPTION_IS_GETTABLE(cap)	(((cap) & (SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE)) == SANE_CAP_SOFT_DETECT)
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_ci/*--------------------------------------------------------------------------*/
81141cc406Sopenharmony_ci
82141cc406Sopenharmony_ci/* Display the message error statistics. */
83141cc406Sopenharmony_cistatic void display_stats(void)
84141cc406Sopenharmony_ci{
85141cc406Sopenharmony_ci#ifdef HAVE_LONG_LONG
86141cc406Sopenharmony_ci	printf("warnings: %d  error: %d  checks: %lld\n",
87141cc406Sopenharmony_ci		   message_number_wrn, message_number_err, checks_done);
88141cc406Sopenharmony_ci#else
89141cc406Sopenharmony_ci	printf("warnings: %d  error: %d  checks: %ld\n",
90141cc406Sopenharmony_ci		   message_number_wrn, message_number_err, checks_done);
91141cc406Sopenharmony_ci#endif
92141cc406Sopenharmony_ci}
93141cc406Sopenharmony_ci
94141cc406Sopenharmony_ci/*
95141cc406Sopenharmony_ci * If the condition is false, display a message with some headers
96141cc406Sopenharmony_ci * depending on the level.
97141cc406Sopenharmony_ci *
98141cc406Sopenharmony_ci * Returns the condition.
99141cc406Sopenharmony_ci *
100141cc406Sopenharmony_ci */
101141cc406Sopenharmony_ci#ifdef __GNUC__
102141cc406Sopenharmony_cistatic int check(enum message_level, int condition, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
103141cc406Sopenharmony_ci#endif
104141cc406Sopenharmony_cistatic int check(enum message_level level, int condition, const char *format, ...)
105141cc406Sopenharmony_ci{
106141cc406Sopenharmony_ci	char str[1000];
107141cc406Sopenharmony_ci	va_list args;
108141cc406Sopenharmony_ci
109141cc406Sopenharmony_ci	if (level != MSG && level != INF) checks_done ++;
110141cc406Sopenharmony_ci
111141cc406Sopenharmony_ci	if (condition != 0)
112141cc406Sopenharmony_ci		return condition;
113141cc406Sopenharmony_ci
114141cc406Sopenharmony_ci	va_start(args, format);
115141cc406Sopenharmony_ci	vsprintf(str, format, args);
116141cc406Sopenharmony_ci	va_end(args);
117141cc406Sopenharmony_ci
118141cc406Sopenharmony_ci	switch(level) {
119141cc406Sopenharmony_ci	case MSG:
120141cc406Sopenharmony_ci		printf("          %s\n", str);
121141cc406Sopenharmony_ci		break;
122141cc406Sopenharmony_ci	case INF:					/* info */
123141cc406Sopenharmony_ci		printf("info    : %s\n", str);
124141cc406Sopenharmony_ci		break;
125141cc406Sopenharmony_ci	case WRN:					/* warning */
126141cc406Sopenharmony_ci		printf("warning : %s\n", str);
127141cc406Sopenharmony_ci		message_number_wrn ++;
128141cc406Sopenharmony_ci		break;
129141cc406Sopenharmony_ci	case ERR:					/* error */
130141cc406Sopenharmony_ci		printf("ERROR   : %s\n", str);
131141cc406Sopenharmony_ci		message_number_err ++;
132141cc406Sopenharmony_ci		break;
133141cc406Sopenharmony_ci	case FATAL:					/* fatal error */
134141cc406Sopenharmony_ci		printf("FATAL ERROR : %s\n", str);
135141cc406Sopenharmony_ci		message_number_err ++;
136141cc406Sopenharmony_ci		break;
137141cc406Sopenharmony_ci	case BUG:					/* bug in tstbackend */
138141cc406Sopenharmony_ci		printf("tstbackend BUG : %s\n", str);
139141cc406Sopenharmony_ci		break;
140141cc406Sopenharmony_ci	}
141141cc406Sopenharmony_ci
142141cc406Sopenharmony_ci	if (level == FATAL || level == BUG) {
143141cc406Sopenharmony_ci		/* Fatal error. Generate a core dump. */
144141cc406Sopenharmony_ci		display_stats();
145141cc406Sopenharmony_ci		abort();
146141cc406Sopenharmony_ci	}
147141cc406Sopenharmony_ci
148141cc406Sopenharmony_ci	fflush(stdout);
149141cc406Sopenharmony_ci
150141cc406Sopenharmony_ci	return(0);
151141cc406Sopenharmony_ci}
152141cc406Sopenharmony_ci
153141cc406Sopenharmony_ci/*--------------------------------------------------------------------------*/
154141cc406Sopenharmony_ci
155141cc406Sopenharmony_ci#define GUARDS_SIZE 4			/* 4 bytes */
156141cc406Sopenharmony_ci#define GUARD1 ((SANE_Word)0x5abf8ea5)
157141cc406Sopenharmony_ci#define GUARD2 ((SANE_Word)0xa58ebf5a)
158141cc406Sopenharmony_ci
159141cc406Sopenharmony_ci/* Allocate the requested memory plus enough room to store some guard bytes. */
160141cc406Sopenharmony_cistatic void *guards_malloc(size_t size)
161141cc406Sopenharmony_ci{
162141cc406Sopenharmony_ci	unsigned char *ptr;
163141cc406Sopenharmony_ci
164141cc406Sopenharmony_ci	size += 2*GUARDS_SIZE;
165141cc406Sopenharmony_ci	ptr = malloc(size);
166141cc406Sopenharmony_ci
167141cc406Sopenharmony_ci	assert(ptr);
168141cc406Sopenharmony_ci
169141cc406Sopenharmony_ci	ptr += GUARDS_SIZE;
170141cc406Sopenharmony_ci
171141cc406Sopenharmony_ci	return(ptr);
172141cc406Sopenharmony_ci}
173141cc406Sopenharmony_ci
174141cc406Sopenharmony_ci/* Free some memory allocated by guards_malloc. */
175141cc406Sopenharmony_cistatic void guards_free(void *ptr)
176141cc406Sopenharmony_ci{
177141cc406Sopenharmony_ci	unsigned char *p = ptr;
178141cc406Sopenharmony_ci
179141cc406Sopenharmony_ci	p -= GUARDS_SIZE;
180141cc406Sopenharmony_ci	free(p);
181141cc406Sopenharmony_ci}
182141cc406Sopenharmony_ci
183141cc406Sopenharmony_ci/* Set the guards */
184141cc406Sopenharmony_cistatic void guards_set(void *ptr, size_t size)
185141cc406Sopenharmony_ci{
186141cc406Sopenharmony_ci	SANE_Word *p;
187141cc406Sopenharmony_ci
188141cc406Sopenharmony_ci	p = (SANE_Word *)(((unsigned char *)ptr) - GUARDS_SIZE);
189141cc406Sopenharmony_ci	*p = GUARD1;
190141cc406Sopenharmony_ci
191141cc406Sopenharmony_ci	p = (SANE_Word *)(((unsigned char *)ptr) + size);
192141cc406Sopenharmony_ci	*p = GUARD2;
193141cc406Sopenharmony_ci}
194141cc406Sopenharmony_ci
195141cc406Sopenharmony_ci/* Check that the guards have not been tampered with. */
196141cc406Sopenharmony_cistatic void guards_check(void *ptr, size_t size)
197141cc406Sopenharmony_ci{
198141cc406Sopenharmony_ci	SANE_Word *p;
199141cc406Sopenharmony_ci
200141cc406Sopenharmony_ci	p = (SANE_Word *)(((unsigned char *)ptr) - GUARDS_SIZE);
201141cc406Sopenharmony_ci	check(FATAL, (*p == GUARD1),
202141cc406Sopenharmony_ci		  "guard before the block has been tampered");
203141cc406Sopenharmony_ci
204141cc406Sopenharmony_ci	p = (SANE_Word *)(((unsigned char *)ptr) + size);
205141cc406Sopenharmony_ci	check(FATAL, (*p == GUARD2),
206141cc406Sopenharmony_ci		  "guard after the block has been tampered");
207141cc406Sopenharmony_ci}
208141cc406Sopenharmony_ci
209141cc406Sopenharmony_ci/*--------------------------------------------------------------------------*/
210141cc406Sopenharmony_ci
211141cc406Sopenharmony_cistatic void
212141cc406Sopenharmony_citest_parameters (SANE_Device * device, SANE_Parameters *params)
213141cc406Sopenharmony_ci{
214141cc406Sopenharmony_ci	SANE_Status status;
215141cc406Sopenharmony_ci	SANE_Parameters p;
216141cc406Sopenharmony_ci
217141cc406Sopenharmony_ci	status = sane_get_parameters (device, &p);
218141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
219141cc406Sopenharmony_ci		  "cannot get the parameters (error %s)", sane_strstatus(status));
220141cc406Sopenharmony_ci
221141cc406Sopenharmony_ci	check(FATAL, ((p.format == SANE_FRAME_GRAY) ||
222141cc406Sopenharmony_ci				  (p.format == SANE_FRAME_RGB) ||
223141cc406Sopenharmony_ci				  (p.format == SANE_FRAME_RED) ||
224141cc406Sopenharmony_ci				  (p.format == SANE_FRAME_GREEN) ||
225141cc406Sopenharmony_ci				  (p.format == SANE_FRAME_BLUE)),
226141cc406Sopenharmony_ci		  "parameter format is not a known SANE_FRAME_* (%d)", p.format);
227141cc406Sopenharmony_ci
228141cc406Sopenharmony_ci	check(FATAL, ((p.last_frame == SANE_FALSE) ||
229141cc406Sopenharmony_ci				  (p.last_frame == SANE_TRUE)),
230141cc406Sopenharmony_ci		  "parameter last_frame is neither SANE_FALSE or SANE_TRUE (%d)", p.last_frame);
231141cc406Sopenharmony_ci
232141cc406Sopenharmony_ci	check(FATAL, ((p.depth == 1) ||
233141cc406Sopenharmony_ci				  (p.depth == 8) ||
234141cc406Sopenharmony_ci				  (p.depth == 16)),
235141cc406Sopenharmony_ci		  "parameter depth is neither 1, 8 or 16 (%d)", p.depth);
236141cc406Sopenharmony_ci
237141cc406Sopenharmony_ci	if (params) {
238141cc406Sopenharmony_ci		*params = p;
239141cc406Sopenharmony_ci	}
240141cc406Sopenharmony_ci}
241141cc406Sopenharmony_ci
242141cc406Sopenharmony_ci/* Try to set every option in a word list. */
243141cc406Sopenharmony_cistatic void
244141cc406Sopenharmony_citest_options_word_list (SANE_Device * device, int option_num,
245141cc406Sopenharmony_ci						const SANE_Option_Descriptor *opt,
246141cc406Sopenharmony_ci						int can_do_recursive)
247141cc406Sopenharmony_ci{
248141cc406Sopenharmony_ci	SANE_Status status;
249141cc406Sopenharmony_ci	int i;
250141cc406Sopenharmony_ci	SANE_Int val_int;
251141cc406Sopenharmony_ci	SANE_Int info;
252141cc406Sopenharmony_ci
253141cc406Sopenharmony_ci	check(FATAL, (opt->type == SANE_TYPE_INT ||
254141cc406Sopenharmony_ci			  opt->type == SANE_TYPE_FIXED),
255141cc406Sopenharmony_ci		  "type must be SANE_TYPE_INT or SANE_TYPE_FIXED (%d)", opt->type);
256141cc406Sopenharmony_ci
257141cc406Sopenharmony_ci	if (!SANE_OPTION_IS_SETTABLE(opt->cap)) return;
258141cc406Sopenharmony_ci
259141cc406Sopenharmony_ci	for (i=1; i<opt->constraint.word_list[0]; i++) {
260141cc406Sopenharmony_ci
261141cc406Sopenharmony_ci		info = 0x1010;			/* garbage */
262141cc406Sopenharmony_ci
263141cc406Sopenharmony_ci		val_int = opt->constraint.word_list[i];
264141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
265141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, &info);
266141cc406Sopenharmony_ci
267141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
268141cc406Sopenharmony_ci			  "cannot set a settable option (status=%s)", sane_strstatus(status));
269141cc406Sopenharmony_ci
270141cc406Sopenharmony_ci		check(WRN, ((info & ~(SANE_INFO_RELOAD_OPTIONS |
271141cc406Sopenharmony_ci							SANE_INFO_RELOAD_PARAMS)) == 0),
272141cc406Sopenharmony_ci			  "sane_control_option set an invalid info (%d)", info);
273141cc406Sopenharmony_ci
274141cc406Sopenharmony_ci		if ((info & SANE_INFO_RELOAD_OPTIONS) && can_do_recursive) {
275141cc406Sopenharmony_ci			test_options(device, can_do_recursive-1);
276141cc406Sopenharmony_ci		}
277141cc406Sopenharmony_ci		if (info & SANE_INFO_RELOAD_PARAMS) {
278141cc406Sopenharmony_ci			test_parameters(device, NULL);
279141cc406Sopenharmony_ci		}
280141cc406Sopenharmony_ci
281141cc406Sopenharmony_ci		/* The option might have become inactive or unsettable. Skip it. */
282141cc406Sopenharmony_ci		if (!SANE_OPTION_IS_ACTIVE(opt->cap) ||
283141cc406Sopenharmony_ci			!SANE_OPTION_IS_SETTABLE(opt->cap))
284141cc406Sopenharmony_ci			return;
285141cc406Sopenharmony_ci
286141cc406Sopenharmony_ci	}
287141cc406Sopenharmony_ci}
288141cc406Sopenharmony_ci
289141cc406Sopenharmony_ci/* Try to set every option in a string list. */
290141cc406Sopenharmony_cistatic void
291141cc406Sopenharmony_citest_options_string_list (SANE_Device * device, int option_num,
292141cc406Sopenharmony_ci						  const SANE_Option_Descriptor *opt,
293141cc406Sopenharmony_ci						  int can_do_recursive)
294141cc406Sopenharmony_ci{
295141cc406Sopenharmony_ci	SANE_Int info;
296141cc406Sopenharmony_ci	SANE_Status status;
297141cc406Sopenharmony_ci	SANE_String val_string;
298141cc406Sopenharmony_ci	int i;
299141cc406Sopenharmony_ci
300141cc406Sopenharmony_ci	check(FATAL, (opt->type == SANE_TYPE_STRING),
301141cc406Sopenharmony_ci		  "type must be SANE_TYPE_STRING (%d)", opt->type);
302141cc406Sopenharmony_ci
303141cc406Sopenharmony_ci	if (!SANE_OPTION_IS_SETTABLE(opt->cap)) return;
304141cc406Sopenharmony_ci
305141cc406Sopenharmony_ci	for (i=0; opt->constraint.string_list[i] != NULL; i++) {
306141cc406Sopenharmony_ci
307141cc406Sopenharmony_ci		val_string = strdup(opt->constraint.string_list[i]);
308141cc406Sopenharmony_ci		assert(val_string);
309141cc406Sopenharmony_ci
310141cc406Sopenharmony_ci		check(WRN, (strlen(val_string) < (size_t)opt->size),
311141cc406Sopenharmony_ci			  "string [%s] is longer than the max size (%d)",
312141cc406Sopenharmony_ci			  val_string, opt->size);
313141cc406Sopenharmony_ci
314141cc406Sopenharmony_ci		info = 0xE1000;			/* garbage */
315141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
316141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, val_string, &info);
317141cc406Sopenharmony_ci
318141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
319141cc406Sopenharmony_ci			  "cannot set a settable option (status=%s)", sane_strstatus(status));
320141cc406Sopenharmony_ci
321141cc406Sopenharmony_ci		check(WRN, ((info & ~(SANE_INFO_RELOAD_OPTIONS |
322141cc406Sopenharmony_ci							SANE_INFO_RELOAD_PARAMS)) == 0),
323141cc406Sopenharmony_ci			  "sane_control_option set an invalid info (%d)", info);
324141cc406Sopenharmony_ci
325141cc406Sopenharmony_ci		free(val_string);
326141cc406Sopenharmony_ci
327141cc406Sopenharmony_ci		if ((info & SANE_INFO_RELOAD_OPTIONS) && can_do_recursive) {
328141cc406Sopenharmony_ci			test_options(device, can_do_recursive-1);
329141cc406Sopenharmony_ci		}
330141cc406Sopenharmony_ci		if (info & SANE_INFO_RELOAD_PARAMS) {
331141cc406Sopenharmony_ci			test_parameters(device, NULL);
332141cc406Sopenharmony_ci		}
333141cc406Sopenharmony_ci
334141cc406Sopenharmony_ci		/* The option might have become inactive or unsettable. Skip it. */
335141cc406Sopenharmony_ci		if (!SANE_OPTION_IS_ACTIVE(opt->cap) ||
336141cc406Sopenharmony_ci			!SANE_OPTION_IS_SETTABLE(opt->cap))
337141cc406Sopenharmony_ci			return;
338141cc406Sopenharmony_ci	}
339141cc406Sopenharmony_ci}
340141cc406Sopenharmony_ci
341141cc406Sopenharmony_ci/* Test the consistency of the options. */
342141cc406Sopenharmony_cistatic void
343141cc406Sopenharmony_citest_options (SANE_Device * device, int can_do_recursive)
344141cc406Sopenharmony_ci{
345141cc406Sopenharmony_ci	SANE_Word info;
346141cc406Sopenharmony_ci	SANE_Int num_dev_options;
347141cc406Sopenharmony_ci	SANE_Status status;
348141cc406Sopenharmony_ci	const SANE_Option_Descriptor *opt;
349141cc406Sopenharmony_ci	int option_num;
350141cc406Sopenharmony_ci	void *optval;				/* value for the option */
351141cc406Sopenharmony_ci	size_t optsize;				/* size of the optval buffer */
352141cc406Sopenharmony_ci
353141cc406Sopenharmony_ci	/*
354141cc406Sopenharmony_ci	 * Test option 0
355141cc406Sopenharmony_ci	 */
356141cc406Sopenharmony_ci	opt = sane_get_option_descriptor (device, 0);
357141cc406Sopenharmony_ci	check(FATAL, (opt != NULL),
358141cc406Sopenharmony_ci		  "cannot get option descriptor for option 0 (it must exist)");
359141cc406Sopenharmony_ci	check(INF, (opt->cap == SANE_CAP_SOFT_DETECT),
360141cc406Sopenharmony_ci		  "invalid capabilities for option 0 (%d)", opt->cap);
361141cc406Sopenharmony_ci	check(ERR, (opt->type == SANE_TYPE_INT),
362141cc406Sopenharmony_ci		  "option 0 type must be SANE_TYPE_INT");
363141cc406Sopenharmony_ci
364141cc406Sopenharmony_ci	/* Get the number of options. */
365141cc406Sopenharmony_ci	status = sane_control_option (device, 0, SANE_ACTION_GET_VALUE, &num_dev_options, 0);
366141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
367141cc406Sopenharmony_ci		  "cannot get option 0 value");
368141cc406Sopenharmony_ci
369141cc406Sopenharmony_ci	/* Try to change the number of options. */
370141cc406Sopenharmony_ci	status = sane_control_option (device, 0, SANE_ACTION_SET_VALUE,
371141cc406Sopenharmony_ci								  &num_dev_options, &info);
372141cc406Sopenharmony_ci	check(WRN, (status != SANE_STATUS_GOOD),
373141cc406Sopenharmony_ci		  "the option 0 value can be set");
374141cc406Sopenharmony_ci
375141cc406Sopenharmony_ci	/*
376141cc406Sopenharmony_ci	 * Test all options
377141cc406Sopenharmony_ci	 */
378141cc406Sopenharmony_ci	option_num = 0;
379141cc406Sopenharmony_ci	for (option_num = 0; option_num < num_dev_options; option_num++) {
380141cc406Sopenharmony_ci
381141cc406Sopenharmony_ci		/* Get the option descriptor */
382141cc406Sopenharmony_ci		opt = sane_get_option_descriptor (device, option_num);
383141cc406Sopenharmony_ci		check(FATAL, (opt != NULL),
384141cc406Sopenharmony_ci			  "cannot get option descriptor for option %d", option_num);
385141cc406Sopenharmony_ci		check(WRN, ((opt->cap & ~(SANE_CAP_SOFT_SELECT |
386141cc406Sopenharmony_ci					SANE_CAP_HARD_SELECT |
387141cc406Sopenharmony_ci					SANE_CAP_SOFT_DETECT |
388141cc406Sopenharmony_ci					SANE_CAP_EMULATED |
389141cc406Sopenharmony_ci					SANE_CAP_AUTOMATIC |
390141cc406Sopenharmony_ci					SANE_CAP_INACTIVE |
391141cc406Sopenharmony_ci					SANE_CAP_ADVANCED)) == 0),
392141cc406Sopenharmony_ci			  "invalid capabilities for option [%d, %s] (%x)", option_num, opt->name, opt->cap);
393141cc406Sopenharmony_ci		check(WRN, (opt->title != NULL),
394141cc406Sopenharmony_ci			  "option [%d, %s] must have a title", option_num, opt->name);
395141cc406Sopenharmony_ci		check(WRN, (opt->desc != NULL),
396141cc406Sopenharmony_ci			  "option [%d, %s] must have a description", option_num, opt->name);
397141cc406Sopenharmony_ci
398141cc406Sopenharmony_ci		if (!SANE_OPTION_IS_ACTIVE (opt->cap)) {
399141cc406Sopenharmony_ci			/* Option not active. Skip the remaining tests. */
400141cc406Sopenharmony_ci			continue;
401141cc406Sopenharmony_ci		}
402141cc406Sopenharmony_ci
403141cc406Sopenharmony_ci		if(verbose_level) {
404141cc406Sopenharmony_ci			printf("checking option ""%s""\n",opt->title);
405141cc406Sopenharmony_ci		}
406141cc406Sopenharmony_ci
407141cc406Sopenharmony_ci		if (opt->type == SANE_TYPE_GROUP) {
408141cc406Sopenharmony_ci			check(INF, (opt->name == NULL || *opt->name == 0),
409141cc406Sopenharmony_ci				  "option [%d, %s] has a name", option_num, opt->name);
410141cc406Sopenharmony_ci			check(ERR, (!SANE_OPTION_IS_SETTABLE (opt->cap)),
411141cc406Sopenharmony_ci				  "option [%d, %s], group option is settable", option_num, opt->name);
412141cc406Sopenharmony_ci		} else {
413141cc406Sopenharmony_ci			if (option_num == 0) {
414141cc406Sopenharmony_ci				check(ERR, (opt->name != NULL && *opt->name ==0),
415141cc406Sopenharmony_ci					  "option 0 must have an empty name (ie. \"\")");
416141cc406Sopenharmony_ci			} else {
417141cc406Sopenharmony_ci				check(ERR, (opt->name != NULL && *opt->name !=0),
418141cc406Sopenharmony_ci					  "option %d must have a name", option_num);
419141cc406Sopenharmony_ci			}
420141cc406Sopenharmony_ci		}
421141cc406Sopenharmony_ci
422141cc406Sopenharmony_ci		/* The option name must contain only "a".."z",
423141cc406Sopenharmony_ci		   "0".."9" and "-" and must start with "a".."z". */
424141cc406Sopenharmony_ci		if (opt->name && opt->name[0]) {
425141cc406Sopenharmony_ci			const char *p = opt->name;
426141cc406Sopenharmony_ci
427141cc406Sopenharmony_ci			check(ERR, (*p >= 'a' && *p <= 'z'),
428141cc406Sopenharmony_ci				  "name for option [%d, %s] must start with in letter in [a..z]",
429141cc406Sopenharmony_ci				  option_num, opt->name);
430141cc406Sopenharmony_ci
431141cc406Sopenharmony_ci			p++;
432141cc406Sopenharmony_ci
433141cc406Sopenharmony_ci			while(*p) {
434141cc406Sopenharmony_ci				check(ERR, ((*p >= 'a' && *p <= 'z') ||
435141cc406Sopenharmony_ci							(*p == '-') ||
436141cc406Sopenharmony_ci							(*p >= '0' && *p <= '9')),
437141cc406Sopenharmony_ci					  "name for option [%d, %s] must only have the letters [-a..z0..9]",
438141cc406Sopenharmony_ci					  option_num, opt->name);
439141cc406Sopenharmony_ci				p++;
440141cc406Sopenharmony_ci			}
441141cc406Sopenharmony_ci		}
442141cc406Sopenharmony_ci
443141cc406Sopenharmony_ci		optval = NULL;
444141cc406Sopenharmony_ci		optsize = 0;
445141cc406Sopenharmony_ci
446141cc406Sopenharmony_ci		switch(opt->type) {
447141cc406Sopenharmony_ci		case SANE_TYPE_BOOL:
448141cc406Sopenharmony_ci			check(WRN, (opt->size == sizeof(SANE_Word)),
449141cc406Sopenharmony_ci				  "size of option %s is incorrect", opt->name);
450141cc406Sopenharmony_ci			optval = guards_malloc(opt->size);
451141cc406Sopenharmony_ci			optsize = opt->size;
452141cc406Sopenharmony_ci			check(WRN, (opt->constraint_type == SANE_CONSTRAINT_NONE),
453141cc406Sopenharmony_ci				  "invalid constraint type for option [%d, %s] (%d)", option_num, opt->name, opt->constraint_type);
454141cc406Sopenharmony_ci			break;
455141cc406Sopenharmony_ci
456141cc406Sopenharmony_ci		case SANE_TYPE_INT:
457141cc406Sopenharmony_ci		case SANE_TYPE_FIXED:
458141cc406Sopenharmony_ci			check(WRN, (opt->size > 0 && (opt->size % sizeof(SANE_Word) == 0)),
459141cc406Sopenharmony_ci				  "invalid size for option %s", opt->name);
460141cc406Sopenharmony_ci			optval = guards_malloc(opt->size);
461141cc406Sopenharmony_ci			optsize = opt->size;
462141cc406Sopenharmony_ci			check(WRN, (opt->constraint_type == SANE_CONSTRAINT_NONE ||
463141cc406Sopenharmony_ci						opt->constraint_type == SANE_CONSTRAINT_RANGE ||
464141cc406Sopenharmony_ci						opt->constraint_type == SANE_CONSTRAINT_WORD_LIST),
465141cc406Sopenharmony_ci				  "invalid constraint type for option [%d, %s] (%d)", option_num, opt->name, opt->constraint_type);
466141cc406Sopenharmony_ci			break;
467141cc406Sopenharmony_ci
468141cc406Sopenharmony_ci		case SANE_TYPE_STRING:
469141cc406Sopenharmony_ci			check(WRN, (opt->size >= 1),
470141cc406Sopenharmony_ci				  "size of option [%d, %s] must be at least 1 for the NUL terminator", option_num, opt->name);
471141cc406Sopenharmony_ci			check(INF, (opt->unit == SANE_UNIT_NONE),
472141cc406Sopenharmony_ci				  "unit of option [%d, %s] is not SANE_UNIT_NONE", option_num, opt->name);
473141cc406Sopenharmony_ci			check(WRN, (opt->constraint_type == SANE_CONSTRAINT_STRING_LIST ||
474141cc406Sopenharmony_ci					  opt->constraint_type == SANE_CONSTRAINT_NONE),
475141cc406Sopenharmony_ci				  "invalid constraint type for option [%d, %s] (%d)", option_num, opt->name, opt->constraint_type);
476141cc406Sopenharmony_ci			optval = guards_malloc(opt->size);
477141cc406Sopenharmony_ci			optsize = opt->size;
478141cc406Sopenharmony_ci			break;
479141cc406Sopenharmony_ci
480141cc406Sopenharmony_ci		case SANE_TYPE_BUTTON:
481141cc406Sopenharmony_ci		case SANE_TYPE_GROUP:
482141cc406Sopenharmony_ci			check(INF, (opt->unit == SANE_UNIT_NONE),
483141cc406Sopenharmony_ci				  "option [%d, %s], unit is not SANE_UNIT_NONE", option_num, opt->name);
484141cc406Sopenharmony_ci			check(INF, (opt->size == 0),
485141cc406Sopenharmony_ci				  "option [%d, %s], size is not 0", option_num, opt->name);
486141cc406Sopenharmony_ci			check(WRN, (opt->constraint_type == SANE_CONSTRAINT_NONE),
487141cc406Sopenharmony_ci				  "invalid constraint type for option [%d, %s] (%d)", option_num, opt->name, opt->constraint_type);
488141cc406Sopenharmony_ci			break;
489141cc406Sopenharmony_ci
490141cc406Sopenharmony_ci		default:
491141cc406Sopenharmony_ci			check(ERR, 0,
492141cc406Sopenharmony_ci				  "invalid type %d for option %s",
493141cc406Sopenharmony_ci				  opt->type, opt->name);
494141cc406Sopenharmony_ci			break;
495141cc406Sopenharmony_ci		}
496141cc406Sopenharmony_ci
497141cc406Sopenharmony_ci		if (optval) {
498141cc406Sopenharmony_ci			/* This is an option with a value */
499141cc406Sopenharmony_ci
500141cc406Sopenharmony_ci			/* get with NULL info.
501141cc406Sopenharmony_ci			 *
502141cc406Sopenharmony_ci			 * The SANE standard is not explicit on that subject. I
503141cc406Sopenharmony_ci			 * consider that an inactive option shouldn't be read by a
504141cc406Sopenharmony_ci			 * frontend because its value is meaningless. I think
505141cc406Sopenharmony_ci			 * that, in that case, SANE_STATUS_INVAL is an appropriate
506141cc406Sopenharmony_ci			 * return.
507141cc406Sopenharmony_ci			 */
508141cc406Sopenharmony_ci			guards_set(optval, optsize);
509141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
510141cc406Sopenharmony_ci										  SANE_ACTION_GET_VALUE, optval, NULL);
511141cc406Sopenharmony_ci			guards_check(optval, optsize);
512141cc406Sopenharmony_ci
513141cc406Sopenharmony_ci			if (SANE_OPTION_IS_GETTABLE (opt->cap)) {
514141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD),
515141cc406Sopenharmony_ci					  "cannot get option [%d, %s] value, although it is active (%s)", option_num, opt->name, sane_strstatus(status));
516141cc406Sopenharmony_ci			} else {
517141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_INVAL),
518141cc406Sopenharmony_ci					  "was able to get option [%d, %s] value, although it is not active", option_num, opt->name);
519141cc406Sopenharmony_ci			}
520141cc406Sopenharmony_ci
521141cc406Sopenharmony_ci			/* set with NULL info */
522141cc406Sopenharmony_ci			guards_set(optval, optsize);
523141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
524141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, optval, NULL);
525141cc406Sopenharmony_ci			guards_check(optval, optsize);
526141cc406Sopenharmony_ci			if (SANE_OPTION_IS_SETTABLE (opt->cap) && SANE_OPTION_IS_ACTIVE (opt->cap)) {
527141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD),
528141cc406Sopenharmony_ci					  "cannot set option [%d, %s] value, although it is active and settable (%s)", option_num, opt->name, sane_strstatus(status));
529141cc406Sopenharmony_ci			} else {
530141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_INVAL),
531141cc406Sopenharmony_ci					  "was able to set option [%d, %s] value, although it is not active or settable", option_num, opt->name);
532141cc406Sopenharmony_ci			}
533141cc406Sopenharmony_ci
534141cc406Sopenharmony_ci			/* Get with invalid info. Since if is a get, info should be either
535141cc406Sopenharmony_ci			 * ignored or set to 0. */
536141cc406Sopenharmony_ci			info = 0xdeadbeef;
537141cc406Sopenharmony_ci			guards_set(optval, optsize);
538141cc406Sopenharmony_ci			status = sane_control_option (device, option_num, SANE_ACTION_GET_VALUE,
539141cc406Sopenharmony_ci										  optval, &info);
540141cc406Sopenharmony_ci			guards_check(optval, optsize);
541141cc406Sopenharmony_ci			if (SANE_OPTION_IS_GETTABLE (opt->cap)) {
542141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD),
543141cc406Sopenharmony_ci					  "cannot get option [%d, %s] value, although it is active (%s)", option_num, opt->name, sane_strstatus(status));
544141cc406Sopenharmony_ci			} else {
545141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_INVAL),
546141cc406Sopenharmony_ci					  "was able to get option [%d, %s] value, although it is not active", option_num, opt->name);
547141cc406Sopenharmony_ci			}
548141cc406Sopenharmony_ci			check(ERR, ((info == (SANE_Int)0xdeadbeef) || (info == 0)),
549141cc406Sopenharmony_ci				  "when getting option [%d, %s], info was set to %x", option_num, opt->name, info);
550141cc406Sopenharmony_ci
551141cc406Sopenharmony_ci			/* Set with invalid info. Info should be reset by the backend. */
552141cc406Sopenharmony_ci			info = 0x10000;
553141cc406Sopenharmony_ci			guards_set(optval, optsize);
554141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
555141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, optval, &info);
556141cc406Sopenharmony_ci			guards_check(optval, optsize);
557141cc406Sopenharmony_ci			if (SANE_OPTION_IS_SETTABLE (opt->cap) && SANE_OPTION_IS_ACTIVE (opt->cap)) {
558141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD),
559141cc406Sopenharmony_ci					  "cannot set option [%d, %s] value, although it is active and settable (%s)", option_num, opt->name, sane_strstatus(status));
560141cc406Sopenharmony_ci
561141cc406Sopenharmony_ci				check(ERR, ((info & ~(SANE_INFO_INEXACT |
562141cc406Sopenharmony_ci									  SANE_INFO_RELOAD_OPTIONS |
563141cc406Sopenharmony_ci									  SANE_INFO_RELOAD_PARAMS)) == 0),
564141cc406Sopenharmony_ci					  "sane_control_option set some wrong bit in info (%d)", info);
565141cc406Sopenharmony_ci
566141cc406Sopenharmony_ci				if (info & SANE_INFO_RELOAD_PARAMS) {
567141cc406Sopenharmony_ci					test_parameters(device, NULL);
568141cc406Sopenharmony_ci				}
569141cc406Sopenharmony_ci			} else {
570141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_INVAL),
571141cc406Sopenharmony_ci					  "was able to set option [%d, %s] value, although it is not active or settable", option_num, opt->name);
572141cc406Sopenharmony_ci			}
573141cc406Sopenharmony_ci
574141cc406Sopenharmony_ci			/* Ask the backend to set the option automatically. */
575141cc406Sopenharmony_ci			guards_set(optval, optsize);
576141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
577141cc406Sopenharmony_ci										  SANE_ACTION_SET_AUTO, optval, &info);
578141cc406Sopenharmony_ci			guards_check(optval, optsize);
579141cc406Sopenharmony_ci			if (SANE_OPTION_IS_SETTABLE (opt->cap) &&
580141cc406Sopenharmony_ci				SANE_OPTION_IS_ACTIVE (opt->cap) &&
581141cc406Sopenharmony_ci				(opt->cap & SANE_CAP_AUTOMATIC)) {
582141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD),
583141cc406Sopenharmony_ci					  "cannot set the option [%d, %s] automatically.", option_num, opt->name);
584141cc406Sopenharmony_ci			} else {
585141cc406Sopenharmony_ci				check(ERR, (status != SANE_STATUS_GOOD),
586141cc406Sopenharmony_ci					  "was able to automatically set option [%d, %s], although it is not active or settable or automatically settable", option_num, opt->name);
587141cc406Sopenharmony_ci			}
588141cc406Sopenharmony_ci			if (info & SANE_INFO_RELOAD_PARAMS) {
589141cc406Sopenharmony_ci				test_parameters(device, NULL);
590141cc406Sopenharmony_ci			}
591141cc406Sopenharmony_ci		}
592141cc406Sopenharmony_ci
593141cc406Sopenharmony_ci		if (optval) {
594141cc406Sopenharmony_ci			guards_free(optval);
595141cc406Sopenharmony_ci			optval = NULL;
596141cc406Sopenharmony_ci		}
597141cc406Sopenharmony_ci
598141cc406Sopenharmony_ci		/* Some capabilities checks. */
599141cc406Sopenharmony_ci		check(ERR, ((opt->cap & (SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_SELECT)) !=
600141cc406Sopenharmony_ci					(SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_SELECT)),
601141cc406Sopenharmony_ci			  "option [%d, %s], SANE_CAP_HARD_SELECT and SANE_CAP_SOFT_SELECT are mutually exclusive", option_num, opt->name);
602141cc406Sopenharmony_ci		if (opt->cap & SANE_CAP_SOFT_SELECT) {
603141cc406Sopenharmony_ci			check(ERR, ((opt->cap & SANE_CAP_SOFT_DETECT) != 0),
604141cc406Sopenharmony_ci				  "option [%d, %s], SANE_CAP_SOFT_DETECT must be set if SANE_CAP_SOFT_SELECT is set", option_num, opt->name);
605141cc406Sopenharmony_ci		}
606141cc406Sopenharmony_ci		if ((opt->cap & (SANE_CAP_SOFT_SELECT |
607141cc406Sopenharmony_ci						 SANE_CAP_HARD_SELECT |
608141cc406Sopenharmony_ci						 SANE_CAP_SOFT_DETECT)) == SANE_CAP_SOFT_DETECT) {
609141cc406Sopenharmony_ci			check(ERR, (!SANE_OPTION_IS_SETTABLE (opt->cap)),
610141cc406Sopenharmony_ci				  "option [%d, %s], must not be settable", option_num, opt->name);
611141cc406Sopenharmony_ci		}
612141cc406Sopenharmony_ci
613141cc406Sopenharmony_ci		if (!SANE_OPTION_IS_SETTABLE (opt->cap)) {
614141cc406Sopenharmony_ci			/* Unsettable option. Ignore the rest of the test. */
615141cc406Sopenharmony_ci			continue;
616141cc406Sopenharmony_ci		}
617141cc406Sopenharmony_ci
618141cc406Sopenharmony_ci		/* Check that will sane_control_option copy the string
619141cc406Sopenharmony_ci		 * parameter and not just store a pointer to it. */
620141cc406Sopenharmony_ci		if (opt->type == SANE_TYPE_STRING) {
621141cc406Sopenharmony_ci			SANE_String val_string2;
622141cc406Sopenharmony_ci			char *optstr;
623141cc406Sopenharmony_ci
624141cc406Sopenharmony_ci			optstr = guards_malloc(opt->size);
625141cc406Sopenharmony_ci			val_string2 = guards_malloc(opt->size);
626141cc406Sopenharmony_ci
627141cc406Sopenharmony_ci			/* Poison the current value. */
628141cc406Sopenharmony_ci			strncpy(optstr, "-pOiSoN-", opt->size-1);
629141cc406Sopenharmony_ci			optstr[opt->size-1] = 0;
630141cc406Sopenharmony_ci
631141cc406Sopenharmony_ci			/* Get the value */
632141cc406Sopenharmony_ci			guards_set(optstr, opt->size);
633141cc406Sopenharmony_ci			status = sane_control_option (device, option_num, SANE_ACTION_GET_VALUE,
634141cc406Sopenharmony_ci										  optstr, NULL);
635141cc406Sopenharmony_ci			guards_check(optstr, opt->size);
636141cc406Sopenharmony_ci			check(FATAL, (status == SANE_STATUS_GOOD),
637141cc406Sopenharmony_ci				  "cannot get option [%d, %s] value", option_num, opt->name);
638141cc406Sopenharmony_ci			check(FATAL, (strcmp(optstr, "-pOiSoN-") != 0),
639141cc406Sopenharmony_ci				  "sane_control_option did not set a value");
640141cc406Sopenharmony_ci
641141cc406Sopenharmony_ci			/* Set the value */
642141cc406Sopenharmony_ci			guards_set(optstr, opt->size);
643141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
644141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, optstr, NULL);
645141cc406Sopenharmony_ci			guards_check(optstr, opt->size);
646141cc406Sopenharmony_ci			check(ERR, (status == SANE_STATUS_GOOD),
647141cc406Sopenharmony_ci				  "cannot set option [%d, %s] value", option_num, opt->name);
648141cc406Sopenharmony_ci
649141cc406Sopenharmony_ci			/* Poison the returned value. */
650141cc406Sopenharmony_ci			strncpy(optstr, "-pOiSoN-", opt->size-1);
651141cc406Sopenharmony_ci			optstr[opt->size-1] = 0;
652141cc406Sopenharmony_ci
653141cc406Sopenharmony_ci			/* Read again the value and compare. */
654141cc406Sopenharmony_ci			guards_set(val_string2, opt->size);
655141cc406Sopenharmony_ci			status = sane_control_option (device, option_num, SANE_ACTION_GET_VALUE,
656141cc406Sopenharmony_ci										  val_string2, NULL);
657141cc406Sopenharmony_ci			guards_check(val_string2, opt->size);
658141cc406Sopenharmony_ci			check(ERR, (status == SANE_STATUS_GOOD),
659141cc406Sopenharmony_ci				  "cannot get option [%d, %s] value", option_num, opt->name);
660141cc406Sopenharmony_ci
661141cc406Sopenharmony_ci			check(FATAL, (strcmp(optstr, val_string2) != 0),
662141cc406Sopenharmony_ci				  "sane_control_option did not copy the string parameter for option [%d, %s]", option_num, opt->name);
663141cc406Sopenharmony_ci
664141cc406Sopenharmony_ci			guards_free(optstr);
665141cc406Sopenharmony_ci			guards_free(val_string2);
666141cc406Sopenharmony_ci		}
667141cc406Sopenharmony_ci
668141cc406Sopenharmony_ci		/* Try both boolean options. */
669141cc406Sopenharmony_ci		if (opt->type == SANE_TYPE_BOOL) {
670141cc406Sopenharmony_ci			SANE_Bool org_v;
671141cc406Sopenharmony_ci			SANE_Bool v;
672141cc406Sopenharmony_ci
673141cc406Sopenharmony_ci			status = sane_control_option (device, option_num, SANE_ACTION_GET_VALUE,
674141cc406Sopenharmony_ci										  &org_v, &info);
675141cc406Sopenharmony_ci			check(ERR, (status == SANE_STATUS_GOOD),
676141cc406Sopenharmony_ci				  "cannot get boolean option [%d, %s] value (%s)", option_num, opt->name, sane_strstatus(status));
677141cc406Sopenharmony_ci			/* Invert the condition. */
678141cc406Sopenharmony_ci			switch(org_v) {
679141cc406Sopenharmony_ci			case SANE_FALSE:
680141cc406Sopenharmony_ci				v = SANE_TRUE;
681141cc406Sopenharmony_ci				break;
682141cc406Sopenharmony_ci			case SANE_TRUE:
683141cc406Sopenharmony_ci				v = SANE_FALSE;
684141cc406Sopenharmony_ci				break;
685141cc406Sopenharmony_ci			default:
686141cc406Sopenharmony_ci				check(ERR, 0,
687141cc406Sopenharmony_ci					  "invalid boolean value %d for option [%d, %s]",
688141cc406Sopenharmony_ci					  org_v, option_num, opt->name);
689141cc406Sopenharmony_ci			}
690141cc406Sopenharmony_ci
691141cc406Sopenharmony_ci			/* Set the opposite of the current value. */
692141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
693141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, &v, &info);
694141cc406Sopenharmony_ci			check(ERR, (status == SANE_STATUS_GOOD),
695141cc406Sopenharmony_ci				  "cannot set boolean option [%d, %s] value (%s)", option_num, opt->name, sane_strstatus(status));
696141cc406Sopenharmony_ci			check(ERR, (v != org_v),
697141cc406Sopenharmony_ci				  "boolean values should be different");
698141cc406Sopenharmony_ci
699141cc406Sopenharmony_ci			if (info & SANE_INFO_RELOAD_PARAMS) {
700141cc406Sopenharmony_ci				test_parameters(device, NULL);
701141cc406Sopenharmony_ci			}
702141cc406Sopenharmony_ci
703141cc406Sopenharmony_ci			/* Set the initial value. */
704141cc406Sopenharmony_ci			v = org_v;
705141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
706141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, &v, &info);
707141cc406Sopenharmony_ci			check(ERR, (status == SANE_STATUS_GOOD),
708141cc406Sopenharmony_ci				  "cannot set boolean option [%d, %s] value (%s)", option_num, opt->name, sane_strstatus(status));
709141cc406Sopenharmony_ci			check(ERR, (v == org_v),
710141cc406Sopenharmony_ci				  "boolean values should be the same");
711141cc406Sopenharmony_ci
712141cc406Sopenharmony_ci			if (info & SANE_INFO_RELOAD_PARAMS) {
713141cc406Sopenharmony_ci				test_parameters(device, NULL);
714141cc406Sopenharmony_ci			}
715141cc406Sopenharmony_ci		}
716141cc406Sopenharmony_ci
717141cc406Sopenharmony_ci		/* Try to set an invalid option. */
718141cc406Sopenharmony_ci		switch(opt->type) {
719141cc406Sopenharmony_ci		case SANE_TYPE_BOOL: {
720141cc406Sopenharmony_ci			SANE_Word v;	/* should be SANE_Bool instead */
721141cc406Sopenharmony_ci
722141cc406Sopenharmony_ci			v = -1;				/* invalid value. must be SANE_FALSE or SANE_TRUE */
723141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
724141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, &v, NULL);
725141cc406Sopenharmony_ci			check(ERR, (status != SANE_STATUS_GOOD),
726141cc406Sopenharmony_ci				  "was able to set an invalid value for boolean option [%d, %s]", option_num, opt->name);
727141cc406Sopenharmony_ci
728141cc406Sopenharmony_ci			v = 2;				/* invalid value. must be SANE_FALSE or SANE_TRUE */
729141cc406Sopenharmony_ci			status = sane_control_option (device, option_num,
730141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, &v, NULL);
731141cc406Sopenharmony_ci			check(ERR, (status != SANE_STATUS_GOOD),
732141cc406Sopenharmony_ci				  "was able to set an invalid value for boolean option [%d, %s]", option_num, opt->name);
733141cc406Sopenharmony_ci		}
734141cc406Sopenharmony_ci		break;
735141cc406Sopenharmony_ci
736141cc406Sopenharmony_ci		case SANE_TYPE_FIXED:
737141cc406Sopenharmony_ci		case SANE_TYPE_INT: {
738141cc406Sopenharmony_ci			SANE_Int *v;
739141cc406Sopenharmony_ci			unsigned int i;
740141cc406Sopenharmony_ci
741141cc406Sopenharmony_ci			v = guards_malloc(opt->size);
742141cc406Sopenharmony_ci
743141cc406Sopenharmony_ci			/* I can only think of a test for
744141cc406Sopenharmony_ci			 * SANE_CONSTRAINT_RANGE. This tests the behaviour of
745141cc406Sopenharmony_ci			 * sanei_constrain_value(). */
746141cc406Sopenharmony_ci			if (opt->constraint_type == SANE_CONSTRAINT_RANGE) {
747141cc406Sopenharmony_ci				for(i=0; i<opt->size / sizeof(SANE_Int); i++)
748141cc406Sopenharmony_ci					v[i] = opt->constraint.range->min - 1;	/* invalid range */
749141cc406Sopenharmony_ci
750141cc406Sopenharmony_ci				guards_set(v, opt->size);
751141cc406Sopenharmony_ci				status = sane_control_option (device, option_num,
752141cc406Sopenharmony_ci											  SANE_ACTION_SET_VALUE, v, &info);
753141cc406Sopenharmony_ci				guards_check(v, opt->size);
754141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD && (info & SANE_INFO_INEXACT) ),
755141cc406Sopenharmony_ci					  "incorrect return when setting an invalid range value for option [%d, %s] (status %s, info %x)", option_num, opt->name, sane_strstatus(status), info);
756141cc406Sopenharmony_ci
757141cc406Sopenharmony_ci				/* Set the corrected value. */
758141cc406Sopenharmony_ci				guards_set(v, opt->size);
759141cc406Sopenharmony_ci				status = sane_control_option (device, option_num,
760141cc406Sopenharmony_ci											  SANE_ACTION_SET_VALUE, v, &info);
761141cc406Sopenharmony_ci				guards_check(v, opt->size);
762141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD && !(info & SANE_INFO_INEXACT) ),
763141cc406Sopenharmony_ci					  "incorrect return when setting an invalid range value for option [%d, %s] (status %s, info %x)", option_num, opt->name, sane_strstatus(status), info);
764141cc406Sopenharmony_ci
765141cc406Sopenharmony_ci
766141cc406Sopenharmony_ci				for(i=0; i<opt->size / sizeof(SANE_Int); i++)
767141cc406Sopenharmony_ci					v[i] = opt->constraint.range->max + 1; /* invalid range */
768141cc406Sopenharmony_ci
769141cc406Sopenharmony_ci				guards_set(v, opt->size);
770141cc406Sopenharmony_ci				status = sane_control_option (device, option_num,
771141cc406Sopenharmony_ci											  SANE_ACTION_SET_VALUE, v, &info);
772141cc406Sopenharmony_ci				guards_check(v, opt->size);
773141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD && (info & SANE_INFO_INEXACT) ),
774141cc406Sopenharmony_ci					  "incorrect return when setting an invalid range value for option [%d, %s] (status %s, info %x)", option_num, opt->name, sane_strstatus(status), info);
775141cc406Sopenharmony_ci
776141cc406Sopenharmony_ci				/* Set the corrected value. */
777141cc406Sopenharmony_ci				guards_set(v, opt->size);
778141cc406Sopenharmony_ci				status = sane_control_option (device, option_num,
779141cc406Sopenharmony_ci											  SANE_ACTION_SET_VALUE, v, &info);
780141cc406Sopenharmony_ci				guards_check(v, opt->size);
781141cc406Sopenharmony_ci				check(ERR, (status == SANE_STATUS_GOOD && !(info & SANE_INFO_INEXACT) ),
782141cc406Sopenharmony_ci					  "incorrect return when setting a valid range value for option [%d, %s] (status %s, info %x)", option_num, opt->name, sane_strstatus(status), info);
783141cc406Sopenharmony_ci			}
784141cc406Sopenharmony_ci
785141cc406Sopenharmony_ci			guards_free(v);
786141cc406Sopenharmony_ci		}
787141cc406Sopenharmony_ci		break;
788141cc406Sopenharmony_ci
789141cc406Sopenharmony_ci		default:
790141cc406Sopenharmony_ci			break;
791141cc406Sopenharmony_ci		}
792141cc406Sopenharmony_ci
793141cc406Sopenharmony_ci		/* TODO: button */
794141cc406Sopenharmony_ci
795141cc406Sopenharmony_ci		/*
796141cc406Sopenharmony_ci		 * Here starts all the recursive stuff. After the test, it is
797141cc406Sopenharmony_ci		 * possible that the value is not settable nor active
798141cc406Sopenharmony_ci		 * anymore.
799141cc406Sopenharmony_ci		 */
800141cc406Sopenharmony_ci
801141cc406Sopenharmony_ci		/* Try to set every option in a list */
802141cc406Sopenharmony_ci		switch(opt->constraint_type) {
803141cc406Sopenharmony_ci		case SANE_CONSTRAINT_WORD_LIST:
804141cc406Sopenharmony_ci			check(FATAL, (opt->constraint.word_list != NULL),
805141cc406Sopenharmony_ci				  "no constraint list for option [%d, %s]", option_num, opt->name);
806141cc406Sopenharmony_ci			test_options_word_list (device, option_num, opt, can_do_recursive);
807141cc406Sopenharmony_ci			break;
808141cc406Sopenharmony_ci
809141cc406Sopenharmony_ci		case SANE_CONSTRAINT_STRING_LIST:
810141cc406Sopenharmony_ci			check(FATAL, (opt->constraint.string_list != NULL),
811141cc406Sopenharmony_ci				  "no constraint list for option [%d, %s]", option_num, opt->name);
812141cc406Sopenharmony_ci			test_options_string_list (device, option_num, opt, can_do_recursive);
813141cc406Sopenharmony_ci			break;
814141cc406Sopenharmony_ci
815141cc406Sopenharmony_ci		case SANE_CONSTRAINT_RANGE:
816141cc406Sopenharmony_ci			check(FATAL, (opt->constraint.range != NULL),
817141cc406Sopenharmony_ci				  "no constraint range for option [%d, %s]", option_num, opt->name);
818141cc406Sopenharmony_ci			check(FATAL, (opt->constraint.range->max >= opt->constraint.range->min),
819141cc406Sopenharmony_ci				  "incorrect range for option [%d, %s] (min=%d > max=%d)",
820141cc406Sopenharmony_ci				  option_num, opt->name, opt->constraint.range->min, opt->constraint.range->max);
821141cc406Sopenharmony_ci			/* Recurse. */
822141cc406Sopenharmony_ci			if (can_do_recursive) {
823141cc406Sopenharmony_ci				test_options(device, can_do_recursive-1);
824141cc406Sopenharmony_ci			}
825141cc406Sopenharmony_ci			break;
826141cc406Sopenharmony_ci
827141cc406Sopenharmony_ci		case SANE_CONSTRAINT_NONE:
828141cc406Sopenharmony_ci			check(INF, (opt->constraint.range == NULL),
829141cc406Sopenharmony_ci				  "option [%d, %s] has some constraint value set", option_num, opt->name);
830141cc406Sopenharmony_ci
831141cc406Sopenharmony_ci			/* Recurse. */
832141cc406Sopenharmony_ci			if (can_do_recursive) {
833141cc406Sopenharmony_ci				test_options(device, can_do_recursive-1);
834141cc406Sopenharmony_ci			}
835141cc406Sopenharmony_ci			break;
836141cc406Sopenharmony_ci		}
837141cc406Sopenharmony_ci
838141cc406Sopenharmony_ci		/* End of the test for that option. */
839141cc406Sopenharmony_ci	}
840141cc406Sopenharmony_ci
841141cc406Sopenharmony_ci	/* test random non-existing options. */
842141cc406Sopenharmony_ci	opt = sane_get_option_descriptor (device, -1);
843141cc406Sopenharmony_ci	check(ERR, (opt == NULL),
844141cc406Sopenharmony_ci		  "was able to get option descriptor for option -1");
845141cc406Sopenharmony_ci
846141cc406Sopenharmony_ci	opt = sane_get_option_descriptor (device, num_dev_options+1);
847141cc406Sopenharmony_ci	check(ERR, (opt == NULL),
848141cc406Sopenharmony_ci		  "was able to get option descriptor for option %d", num_dev_options+1);
849141cc406Sopenharmony_ci
850141cc406Sopenharmony_ci	opt = sane_get_option_descriptor (device, num_dev_options+2);
851141cc406Sopenharmony_ci	check(ERR, (opt == NULL),
852141cc406Sopenharmony_ci		  "was able to get option descriptor for option %d", num_dev_options+2);
853141cc406Sopenharmony_ci
854141cc406Sopenharmony_ci	opt = sane_get_option_descriptor (device, num_dev_options+50);
855141cc406Sopenharmony_ci	check(ERR, (opt == NULL),
856141cc406Sopenharmony_ci		  "was able to get option descriptor for option %d", num_dev_options+50);
857141cc406Sopenharmony_ci}
858141cc406Sopenharmony_ci
859141cc406Sopenharmony_ci/* Get an option descriptor by the name of the option. */
860141cc406Sopenharmony_cistatic const SANE_Option_Descriptor *get_optdesc_by_name(SANE_Handle device, const char *name, int *option_num)
861141cc406Sopenharmony_ci{
862141cc406Sopenharmony_ci	const SANE_Option_Descriptor *opt;
863141cc406Sopenharmony_ci	SANE_Int num_dev_options;
864141cc406Sopenharmony_ci	SANE_Status status;
865141cc406Sopenharmony_ci
866141cc406Sopenharmony_ci	/* Get the number of options. */
867141cc406Sopenharmony_ci	status = sane_control_option (device, 0, SANE_ACTION_GET_VALUE, &num_dev_options, 0);
868141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
869141cc406Sopenharmony_ci		  "cannot get option 0 value (%s)", sane_strstatus(status));
870141cc406Sopenharmony_ci
871141cc406Sopenharmony_ci	for (*option_num = 0; *option_num < num_dev_options; (*option_num)++) {
872141cc406Sopenharmony_ci
873141cc406Sopenharmony_ci		/* Get the option descriptor */
874141cc406Sopenharmony_ci		opt = sane_get_option_descriptor (device, *option_num);
875141cc406Sopenharmony_ci		check(FATAL, (opt != NULL),
876141cc406Sopenharmony_ci			  "cannot get option descriptor for option %d", *option_num);
877141cc406Sopenharmony_ci
878141cc406Sopenharmony_ci		if (opt->name && strcmp(opt->name, name) == 0) {
879141cc406Sopenharmony_ci			return(opt);
880141cc406Sopenharmony_ci		}
881141cc406Sopenharmony_ci	}
882141cc406Sopenharmony_ci	return(NULL);
883141cc406Sopenharmony_ci}
884141cc406Sopenharmony_ci
885141cc406Sopenharmony_ci/* Set the first value for an option. That equates to the minimum for a
886141cc406Sopenharmony_ci * range or the first element in a list. */
887141cc406Sopenharmony_cistatic void set_min_value(SANE_Handle device, int option_num,
888141cc406Sopenharmony_ci						  const SANE_Option_Descriptor *opt)
889141cc406Sopenharmony_ci{
890141cc406Sopenharmony_ci	SANE_Status status;
891141cc406Sopenharmony_ci	SANE_String val_string;
892141cc406Sopenharmony_ci	SANE_Int val_int;
893141cc406Sopenharmony_ci	int rc;
894141cc406Sopenharmony_ci
895141cc406Sopenharmony_ci	check(BUG, (SANE_OPTION_IS_SETTABLE(opt->cap)),
896141cc406Sopenharmony_ci		  "option is not settable");
897141cc406Sopenharmony_ci
898141cc406Sopenharmony_ci	switch(opt->constraint_type) {
899141cc406Sopenharmony_ci	case SANE_CONSTRAINT_WORD_LIST:
900141cc406Sopenharmony_ci		rc = check(ERR, (opt->constraint.word_list[0] > 0),
901141cc406Sopenharmony_ci				   "no value in the list for option %s", opt->name);
902141cc406Sopenharmony_ci		if (!rc) return;
903141cc406Sopenharmony_ci		val_int = opt->constraint.word_list[1];
904141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
905141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, NULL);
906141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
907141cc406Sopenharmony_ci			  "cannot set option %s to %d (%s)", opt->name, val_int, sane_strstatus(status));
908141cc406Sopenharmony_ci		break;
909141cc406Sopenharmony_ci
910141cc406Sopenharmony_ci	case SANE_CONSTRAINT_STRING_LIST:
911141cc406Sopenharmony_ci		rc = check(ERR, (opt->constraint.string_list[0] != NULL),
912141cc406Sopenharmony_ci				   "no value in the list for option %s", opt->name);
913141cc406Sopenharmony_ci		if (!rc) return;
914141cc406Sopenharmony_ci		val_string = strdup(opt->constraint.string_list[0]);
915141cc406Sopenharmony_ci		assert(val_string);
916141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
917141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, val_string, NULL);
918141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
919141cc406Sopenharmony_ci			  "cannot set option %s to [%s] (%s)", opt->name, val_string, sane_strstatus(status));
920141cc406Sopenharmony_ci		free(val_string);
921141cc406Sopenharmony_ci		break;
922141cc406Sopenharmony_ci
923141cc406Sopenharmony_ci	case SANE_CONSTRAINT_RANGE:
924141cc406Sopenharmony_ci		val_int = opt->constraint.range->min;
925141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
926141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, NULL);
927141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
928141cc406Sopenharmony_ci			  "cannot set option %s to %d (%s)", opt->name, val_int, sane_strstatus(status));
929141cc406Sopenharmony_ci		break;
930141cc406Sopenharmony_ci
931141cc406Sopenharmony_ci	default:
932141cc406Sopenharmony_ci		abort();
933141cc406Sopenharmony_ci	}
934141cc406Sopenharmony_ci}
935141cc406Sopenharmony_ci
936141cc406Sopenharmony_ci/* Set the last value for an option. That equates to the maximum for a
937141cc406Sopenharmony_ci * range or the last element in a list. */
938141cc406Sopenharmony_cistatic void set_max_value(SANE_Handle device, int option_num,
939141cc406Sopenharmony_ci						  const SANE_Option_Descriptor *opt)
940141cc406Sopenharmony_ci{
941141cc406Sopenharmony_ci	SANE_Status status;
942141cc406Sopenharmony_ci	SANE_String val_string;
943141cc406Sopenharmony_ci	SANE_Int val_int;
944141cc406Sopenharmony_ci	int i;
945141cc406Sopenharmony_ci	int rc;
946141cc406Sopenharmony_ci
947141cc406Sopenharmony_ci	check(BUG, (SANE_OPTION_IS_SETTABLE(opt->cap)),
948141cc406Sopenharmony_ci		  "option is not settable");
949141cc406Sopenharmony_ci
950141cc406Sopenharmony_ci	switch(opt->constraint_type) {
951141cc406Sopenharmony_ci	case SANE_CONSTRAINT_WORD_LIST:
952141cc406Sopenharmony_ci		rc = check(ERR, (opt->constraint.word_list[0] > 0),
953141cc406Sopenharmony_ci				   "no value in the list for option %s", opt->name);
954141cc406Sopenharmony_ci		if (!rc) return;
955141cc406Sopenharmony_ci		val_int = opt->constraint.word_list[opt->constraint.word_list[0]];
956141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
957141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, NULL);
958141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
959141cc406Sopenharmony_ci			  "cannot set option %s to %d (%s)", opt->name, val_int, sane_strstatus(status));
960141cc406Sopenharmony_ci		break;
961141cc406Sopenharmony_ci
962141cc406Sopenharmony_ci	case SANE_CONSTRAINT_STRING_LIST:
963141cc406Sopenharmony_ci		rc = check(ERR, (opt->constraint.string_list[0] != NULL),
964141cc406Sopenharmony_ci				   "no value in the list for option %s", opt->name);
965141cc406Sopenharmony_ci		if (!rc) return;
966141cc406Sopenharmony_ci		for (i=1; opt->constraint.string_list[i] != NULL; i++);
967141cc406Sopenharmony_ci		val_string = strdup(opt->constraint.string_list[i-1]);
968141cc406Sopenharmony_ci		assert(val_string);
969141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
970141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, val_string, NULL);
971141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
972141cc406Sopenharmony_ci			  "cannot set option %s to [%s] (%s)", opt->name, val_string, sane_strstatus(status));
973141cc406Sopenharmony_ci		free(val_string);
974141cc406Sopenharmony_ci		break;
975141cc406Sopenharmony_ci
976141cc406Sopenharmony_ci	case SANE_CONSTRAINT_RANGE:
977141cc406Sopenharmony_ci		val_int = opt->constraint.range->max;
978141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
979141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, NULL);
980141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
981141cc406Sopenharmony_ci			  "cannot set option %s to %d (%s)", opt->name, val_int, sane_strstatus(status));
982141cc406Sopenharmony_ci		break;
983141cc406Sopenharmony_ci
984141cc406Sopenharmony_ci	default:
985141cc406Sopenharmony_ci		abort();
986141cc406Sopenharmony_ci	}
987141cc406Sopenharmony_ci}
988141cc406Sopenharmony_ci
989141cc406Sopenharmony_ci/* Set a random value for an option amongst the possible values. */
990141cc406Sopenharmony_cistatic void set_random_value(SANE_Handle device, int option_num,
991141cc406Sopenharmony_ci							 const SANE_Option_Descriptor *opt)
992141cc406Sopenharmony_ci{
993141cc406Sopenharmony_ci	SANE_Status status;
994141cc406Sopenharmony_ci	SANE_String val_string;
995141cc406Sopenharmony_ci	SANE_Int val_int;
996141cc406Sopenharmony_ci	int i;
997141cc406Sopenharmony_ci	int rc;
998141cc406Sopenharmony_ci
999141cc406Sopenharmony_ci	check(BUG, (SANE_OPTION_IS_SETTABLE(opt->cap)),
1000141cc406Sopenharmony_ci		  "option is not settable");
1001141cc406Sopenharmony_ci
1002141cc406Sopenharmony_ci	switch(opt->constraint_type) {
1003141cc406Sopenharmony_ci	case SANE_CONSTRAINT_WORD_LIST:
1004141cc406Sopenharmony_ci		rc = check(ERR, (opt->constraint.word_list[0] > 0),
1005141cc406Sopenharmony_ci				   "no value in the list for option %s", opt->name);
1006141cc406Sopenharmony_ci		if (!rc) return;
1007141cc406Sopenharmony_ci		i=1+(rand() % opt->constraint.word_list[0]);
1008141cc406Sopenharmony_ci		val_int = opt->constraint.word_list[i];
1009141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
1010141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, NULL);
1011141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
1012141cc406Sopenharmony_ci			  "cannot set option %s to %d (%s)", opt->name, val_int, sane_strstatus(status));
1013141cc406Sopenharmony_ci		break;
1014141cc406Sopenharmony_ci
1015141cc406Sopenharmony_ci	case SANE_CONSTRAINT_STRING_LIST:
1016141cc406Sopenharmony_ci		rc = check(ERR, (opt->constraint.string_list[0] != NULL),
1017141cc406Sopenharmony_ci				   "no value in the list for option %s", opt->name);
1018141cc406Sopenharmony_ci		if (!rc) return;
1019141cc406Sopenharmony_ci		for (i=0; opt->constraint.string_list[i] != NULL; i++);
1020141cc406Sopenharmony_ci		i = rand() % i;
1021141cc406Sopenharmony_ci		val_string = strdup(opt->constraint.string_list[0]);
1022141cc406Sopenharmony_ci		assert(val_string);
1023141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
1024141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, val_string, NULL);
1025141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
1026141cc406Sopenharmony_ci			  "cannot set option %s to [%s] (%s)", opt->name, val_string, sane_strstatus(status));
1027141cc406Sopenharmony_ci		free(val_string);
1028141cc406Sopenharmony_ci		break;
1029141cc406Sopenharmony_ci
1030141cc406Sopenharmony_ci	case SANE_CONSTRAINT_RANGE:
1031141cc406Sopenharmony_ci		i = opt->constraint.range->max - opt->constraint.range->min;
1032141cc406Sopenharmony_ci		i = rand() % i;
1033141cc406Sopenharmony_ci		val_int = opt->constraint.range->min + i;
1034141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
1035141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE, &val_int, NULL);
1036141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
1037141cc406Sopenharmony_ci			  "cannot set option %s to %d (%s)", opt->name, val_int, sane_strstatus(status));
1038141cc406Sopenharmony_ci		break;
1039141cc406Sopenharmony_ci
1040141cc406Sopenharmony_ci	default:
1041141cc406Sopenharmony_ci		abort();
1042141cc406Sopenharmony_ci	}
1043141cc406Sopenharmony_ci}
1044141cc406Sopenharmony_ci
1045141cc406Sopenharmony_ci/*--------------------------------------------------------------------------*/
1046141cc406Sopenharmony_ci
1047141cc406Sopenharmony_ci/* Returns a string with the value of an option. */
1048141cc406Sopenharmony_cistatic char *get_option_value(SANE_Handle device, const char *option_name)
1049141cc406Sopenharmony_ci{
1050141cc406Sopenharmony_ci	const SANE_Option_Descriptor *opt;
1051141cc406Sopenharmony_ci	void *optval;				/* value for the option */
1052141cc406Sopenharmony_ci	int optnum;
1053141cc406Sopenharmony_ci	static char str[100];
1054141cc406Sopenharmony_ci	SANE_Status status;
1055141cc406Sopenharmony_ci
1056141cc406Sopenharmony_ci	opt = get_optdesc_by_name(device, option_name, &optnum);
1057141cc406Sopenharmony_ci	if (opt) {
1058141cc406Sopenharmony_ci
1059141cc406Sopenharmony_ci		optval = guards_malloc(opt->size);
1060141cc406Sopenharmony_ci		status = sane_control_option (device, optnum,
1061141cc406Sopenharmony_ci									  SANE_ACTION_GET_VALUE, optval, NULL);
1062141cc406Sopenharmony_ci
1063141cc406Sopenharmony_ci		if (status == SANE_STATUS_GOOD) {
1064141cc406Sopenharmony_ci			switch(opt->type) {
1065141cc406Sopenharmony_ci
1066141cc406Sopenharmony_ci			case SANE_TYPE_BOOL:
1067141cc406Sopenharmony_ci				if (*(SANE_Word*) optval == SANE_FALSE) {
1068141cc406Sopenharmony_ci					strcpy(str, "FALSE");
1069141cc406Sopenharmony_ci				} else {
1070141cc406Sopenharmony_ci					strcpy(str, "TRUE");
1071141cc406Sopenharmony_ci				}
1072141cc406Sopenharmony_ci				break;
1073141cc406Sopenharmony_ci
1074141cc406Sopenharmony_ci			case SANE_TYPE_INT:
1075141cc406Sopenharmony_ci				sprintf(str, "%d", *(SANE_Word*) optval);
1076141cc406Sopenharmony_ci				break;
1077141cc406Sopenharmony_ci
1078141cc406Sopenharmony_ci			case SANE_TYPE_FIXED: {
1079141cc406Sopenharmony_ci				int i;
1080141cc406Sopenharmony_ci				i = SANE_UNFIX(*(SANE_Word*) optval);
1081141cc406Sopenharmony_ci				sprintf(str, "%d", i);
1082141cc406Sopenharmony_ci			}
1083141cc406Sopenharmony_ci			break;
1084141cc406Sopenharmony_ci
1085141cc406Sopenharmony_ci			case SANE_TYPE_STRING:
1086141cc406Sopenharmony_ci				strcpy(str, optval);
1087141cc406Sopenharmony_ci				break;
1088141cc406Sopenharmony_ci
1089141cc406Sopenharmony_ci			default:
1090141cc406Sopenharmony_ci				str[0] = 0;
1091141cc406Sopenharmony_ci			}
1092141cc406Sopenharmony_ci		} else {
1093141cc406Sopenharmony_ci			/* Shouldn't happen. */
1094141cc406Sopenharmony_ci			strcpy(str, "backend default");
1095141cc406Sopenharmony_ci		}
1096141cc406Sopenharmony_ci
1097141cc406Sopenharmony_ci		guards_free(optval);
1098141cc406Sopenharmony_ci
1099141cc406Sopenharmony_ci	} else {
1100141cc406Sopenharmony_ci		/* The option does not exists. */
1101141cc406Sopenharmony_ci		strcpy(str, "backend default");
1102141cc406Sopenharmony_ci	}
1103141cc406Sopenharmony_ci
1104141cc406Sopenharmony_ci	return(str);
1105141cc406Sopenharmony_ci}
1106141cc406Sopenharmony_ci
1107141cc406Sopenharmony_ci/* Display the parameters that used for a scan. */
1108141cc406Sopenharmony_cistatic char *display_scan_parameters(SANE_Handle device)
1109141cc406Sopenharmony_ci{
1110141cc406Sopenharmony_ci	static char str[150];
1111141cc406Sopenharmony_ci	char *p = str;
1112141cc406Sopenharmony_ci
1113141cc406Sopenharmony_ci	*p = 0;
1114141cc406Sopenharmony_ci
1115141cc406Sopenharmony_ci	p += sprintf(p, "scan mode=[%s] ", get_option_value(device, SANE_NAME_SCAN_MODE));
1116141cc406Sopenharmony_ci	p += sprintf(p, "resolution=[%s] ", get_option_value(device, SANE_NAME_SCAN_RESOLUTION));
1117141cc406Sopenharmony_ci
1118141cc406Sopenharmony_ci	p += sprintf(p, "tl_x=[%s] ", get_option_value(device, SANE_NAME_SCAN_TL_X));
1119141cc406Sopenharmony_ci	p += sprintf(p, "tl_y=[%s] ", get_option_value(device, SANE_NAME_SCAN_TL_Y));
1120141cc406Sopenharmony_ci	p += sprintf(p, "br_x=[%s] ", get_option_value(device, SANE_NAME_SCAN_BR_X));
1121141cc406Sopenharmony_ci	p += sprintf(p, "br_y=[%s] ", get_option_value(device, SANE_NAME_SCAN_BR_Y));
1122141cc406Sopenharmony_ci
1123141cc406Sopenharmony_ci	return(str);
1124141cc406Sopenharmony_ci}
1125141cc406Sopenharmony_ci
1126141cc406Sopenharmony_ci/* Do a scan to test the correctness of the backend. */
1127141cc406Sopenharmony_cistatic void test_scan(SANE_Handle device)
1128141cc406Sopenharmony_ci{
1129141cc406Sopenharmony_ci	const SANE_Option_Descriptor *opt;
1130141cc406Sopenharmony_ci	SANE_Status status;
1131141cc406Sopenharmony_ci	int option_num;
1132141cc406Sopenharmony_ci	SANE_Int val_int;
1133141cc406Sopenharmony_ci	unsigned char *image = NULL;
1134141cc406Sopenharmony_ci	SANE_Parameters params;
1135141cc406Sopenharmony_ci	size_t to_read;
1136141cc406Sopenharmony_ci	SANE_Int len=0;
1137141cc406Sopenharmony_ci	int ask_len;
1138141cc406Sopenharmony_ci	int rc;
1139141cc406Sopenharmony_ci	int fd;
1140141cc406Sopenharmony_ci
1141141cc406Sopenharmony_ci	/* Set the largest scan possible.
1142141cc406Sopenharmony_ci	 *
1143141cc406Sopenharmony_ci	 * For that test, the corner
1144141cc406Sopenharmony_ci	 * position must exists and be SANE_CONSTRAINT_RANGE (this is not
1145141cc406Sopenharmony_ci	 * a SANE requirement though).
1146141cc406Sopenharmony_ci	 */
1147141cc406Sopenharmony_ci	opt = get_optdesc_by_name(device, SANE_NAME_SCAN_TL_X, &option_num);
1148141cc406Sopenharmony_ci	if (opt) set_min_value(device, option_num, opt);
1149141cc406Sopenharmony_ci
1150141cc406Sopenharmony_ci	opt = get_optdesc_by_name(device, SANE_NAME_SCAN_TL_Y, &option_num);
1151141cc406Sopenharmony_ci	if (opt) set_min_value(device, option_num, opt);
1152141cc406Sopenharmony_ci
1153141cc406Sopenharmony_ci	opt = get_optdesc_by_name(device, SANE_NAME_SCAN_BR_X, &option_num);
1154141cc406Sopenharmony_ci	if (opt) set_max_value(device, option_num, opt);
1155141cc406Sopenharmony_ci
1156141cc406Sopenharmony_ci	opt = get_optdesc_by_name(device, SANE_NAME_SCAN_BR_Y, &option_num);
1157141cc406Sopenharmony_ci	if (opt) set_max_value(device, option_num, opt);
1158141cc406Sopenharmony_ci
1159141cc406Sopenharmony_ci#define IMAGE_SIZE (512 * 1024)
1160141cc406Sopenharmony_ci	image = guards_malloc(IMAGE_SIZE);
1161141cc406Sopenharmony_ci
1162141cc406Sopenharmony_ci	/* Try a read outside of a scan. */
1163141cc406Sopenharmony_ci	status = sane_read (device, image, len, &len);
1164141cc406Sopenharmony_ci	check(ERR, (status != SANE_STATUS_GOOD),
1165141cc406Sopenharmony_ci		  "it is possible to sane_read outside of a scan");
1166141cc406Sopenharmony_ci
1167141cc406Sopenharmony_ci	/* Try to set the I/O mode outside of a scan. */
1168141cc406Sopenharmony_ci	status = sane_set_io_mode (device, SANE_FALSE);
1169141cc406Sopenharmony_ci	check(ERR, (status == SANE_STATUS_INVAL),
1170141cc406Sopenharmony_ci		  "it is possible to sane_set_io_mode outside of a scan");
1171141cc406Sopenharmony_ci	status = sane_set_io_mode (device, SANE_TRUE);
1172141cc406Sopenharmony_ci	check(ERR, (status == SANE_STATUS_INVAL ||
1173141cc406Sopenharmony_ci				status == SANE_STATUS_UNSUPPORTED),
1174141cc406Sopenharmony_ci		  "it is possible to sane_set_io_mode outside of a scan");
1175141cc406Sopenharmony_ci
1176141cc406Sopenharmony_ci	/* Test sane_get_select_fd outside of a scan. */
1177141cc406Sopenharmony_ci	status = sane_get_select_fd(device, &fd);
1178141cc406Sopenharmony_ci	check(ERR, (status == SANE_STATUS_INVAL ||
1179141cc406Sopenharmony_ci				status == SANE_STATUS_UNSUPPORTED),
1180141cc406Sopenharmony_ci		  "sane_get_select_fd outside of a scan returned an invalid status (%s)",
1181141cc406Sopenharmony_ci		  sane_strstatus (status));
1182141cc406Sopenharmony_ci
1183141cc406Sopenharmony_ci	if (test_level > 2) {
1184141cc406Sopenharmony_ci		/* Do a scan, reading byte per byte */
1185141cc406Sopenharmony_ci		check(MSG, 0, "TEST: scan byte per byte - %s", display_scan_parameters(device));
1186141cc406Sopenharmony_ci
1187141cc406Sopenharmony_ci		test_parameters(device, &params);
1188141cc406Sopenharmony_ci		status = sane_start (device);
1189141cc406Sopenharmony_ci		rc = check(ERR, (status == SANE_STATUS_GOOD),
1190141cc406Sopenharmony_ci				   "cannot start the scan (%s)", sane_strstatus (status));
1191141cc406Sopenharmony_ci		if (!rc) goto the_end;
1192141cc406Sopenharmony_ci
1193141cc406Sopenharmony_ci		/* sane_set_io_mode with SANE_FALSE is always supported. */
1194141cc406Sopenharmony_ci		status = sane_set_io_mode (device, SANE_FALSE);
1195141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
1196141cc406Sopenharmony_ci			  "sane_set_io_mode with SANE_FALSE must return SANE_STATUS_GOOD");
1197141cc406Sopenharmony_ci
1198141cc406Sopenharmony_ci		/* test sane_set_io_mode with SANE_TRUE. */
1199141cc406Sopenharmony_ci		status = sane_set_io_mode (device, SANE_TRUE);
1200141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD ||
1201141cc406Sopenharmony_ci					status == SANE_STATUS_UNSUPPORTED),
1202141cc406Sopenharmony_ci			  "sane_set_io_mode with SANE_TRUE returned an invalid status (%s)",
1203141cc406Sopenharmony_ci			  sane_strstatus (status));
1204141cc406Sopenharmony_ci
1205141cc406Sopenharmony_ci		/* Put the backend back into blocking mode. */
1206141cc406Sopenharmony_ci		status = sane_set_io_mode (device, SANE_FALSE);
1207141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD),
1208141cc406Sopenharmony_ci			  "sane_set_io_mode with SANE_FALSE must return SANE_STATUS_GOOD");
1209141cc406Sopenharmony_ci
1210141cc406Sopenharmony_ci		/* Test sane_get_select_fd */
1211141cc406Sopenharmony_ci		fd = 0x76575;				/* won't exists */
1212141cc406Sopenharmony_ci		status = sane_get_select_fd(device, &fd);
1213141cc406Sopenharmony_ci		check(ERR, (status == SANE_STATUS_GOOD ||
1214141cc406Sopenharmony_ci					status == SANE_STATUS_UNSUPPORTED),
1215141cc406Sopenharmony_ci			  "sane_get_select_fd returned an invalid status (%s)",
1216141cc406Sopenharmony_ci			  sane_strstatus (status));
1217141cc406Sopenharmony_ci		if (status == SANE_STATUS_GOOD) {
1218141cc406Sopenharmony_ci			check(ERR, (fd != 0x76575),
1219141cc406Sopenharmony_ci				  "sane_get_select_fd didn't set the fd although it should have");
1220141cc406Sopenharmony_ci			check(ERR, (fd >= 0),
1221141cc406Sopenharmony_ci				  "sane_get_select_fd returned an invalid fd");
1222141cc406Sopenharmony_ci		}
1223141cc406Sopenharmony_ci
1224141cc406Sopenharmony_ci		/* Check that it is not possible to set an option. It is probably
1225141cc406Sopenharmony_ci		 * a requirement stated indirectly in the section 4.4 on code
1226141cc406Sopenharmony_ci		 * flow.
1227141cc406Sopenharmony_ci		 */
1228141cc406Sopenharmony_ci		status = sane_control_option (device, option_num,
1229141cc406Sopenharmony_ci									  SANE_ACTION_SET_VALUE,
1230141cc406Sopenharmony_ci									  &val_int , NULL);
1231141cc406Sopenharmony_ci		check(WRN, (status != SANE_STATUS_GOOD),
1232141cc406Sopenharmony_ci			  "it is possible to set a value during a scan");
1233141cc406Sopenharmony_ci
1234141cc406Sopenharmony_ci		test_parameters(device, &params);
1235141cc406Sopenharmony_ci
1236141cc406Sopenharmony_ci		if (params.bytes_per_line != 0 && params.lines != 0) {
1237141cc406Sopenharmony_ci
1238141cc406Sopenharmony_ci			to_read = params.bytes_per_line * params.lines;
1239141cc406Sopenharmony_ci			while(SANE_TRUE) {
1240141cc406Sopenharmony_ci				len = 76457645;		/* garbage */
1241141cc406Sopenharmony_ci				guards_set(image, 1);
1242141cc406Sopenharmony_ci				status = sane_read (device, image, 1, &len);
1243141cc406Sopenharmony_ci				guards_check(image, 1);
1244141cc406Sopenharmony_ci
1245141cc406Sopenharmony_ci				if (status == SANE_STATUS_EOF) {
1246141cc406Sopenharmony_ci					/* End of scan */
1247141cc406Sopenharmony_ci					check(ERR, (len == 0),
1248141cc406Sopenharmony_ci						  "the length returned is not 0");
1249141cc406Sopenharmony_ci					break;
1250141cc406Sopenharmony_ci				}
1251141cc406Sopenharmony_ci
1252141cc406Sopenharmony_ci				rc = check(ERR, (status == SANE_STATUS_GOOD),
1253141cc406Sopenharmony_ci						   "scan stopped - status is %s", sane_strstatus (status));
1254141cc406Sopenharmony_ci				if (!rc) {
1255141cc406Sopenharmony_ci					check(ERR, (len == 0),
1256141cc406Sopenharmony_ci						  "the length returned is not 0");
1257141cc406Sopenharmony_ci					break;
1258141cc406Sopenharmony_ci				}
1259141cc406Sopenharmony_ci
1260141cc406Sopenharmony_ci				/* The scanner can only return 1. If it returns 0, we may
1261141cc406Sopenharmony_ci				 * loop forever. */
1262141cc406Sopenharmony_ci				rc = check(ERR, (len == 1),
1263141cc406Sopenharmony_ci						   "backend returned 0 bytes - skipping test");
1264141cc406Sopenharmony_ci				if (!rc) {
1265141cc406Sopenharmony_ci					break;
1266141cc406Sopenharmony_ci				}
1267141cc406Sopenharmony_ci
1268141cc406Sopenharmony_ci				to_read -= len;
1269141cc406Sopenharmony_ci			}
1270141cc406Sopenharmony_ci
1271141cc406Sopenharmony_ci			if (params.lines != -1) {
1272141cc406Sopenharmony_ci				check(ERR, (to_read == 0),
1273141cc406Sopenharmony_ci					  "scan ended, but data was truncated");
1274141cc406Sopenharmony_ci			}
1275141cc406Sopenharmony_ci		}
1276141cc406Sopenharmony_ci
1277141cc406Sopenharmony_ci		sane_cancel(device);
1278141cc406Sopenharmony_ci	}
1279141cc406Sopenharmony_ci
1280141cc406Sopenharmony_ci	/* Try a read outside a scan. */
1281141cc406Sopenharmony_ci	ask_len = 1;
1282141cc406Sopenharmony_ci	guards_set(image, ask_len);
1283141cc406Sopenharmony_ci	status = sane_read (device, image, ask_len, &len);
1284141cc406Sopenharmony_ci	guards_check(image, ask_len);
1285141cc406Sopenharmony_ci	check(ERR, (status != SANE_STATUS_GOOD),
1286141cc406Sopenharmony_ci		  "it is possible to sane_read outside a scan");
1287141cc406Sopenharmony_ci
1288141cc406Sopenharmony_ci
1289141cc406Sopenharmony_ci	/*
1290141cc406Sopenharmony_ci	 * Do a partial scan
1291141cc406Sopenharmony_ci	 */
1292141cc406Sopenharmony_ci	check(MSG, 0, "TEST: partial scan - %s", display_scan_parameters(device));
1293141cc406Sopenharmony_ci
1294141cc406Sopenharmony_ci	status = sane_start (device);
1295141cc406Sopenharmony_ci	rc = check(ERR, (status == SANE_STATUS_GOOD),
1296141cc406Sopenharmony_ci			   "cannot start the scan (%s)", sane_strstatus (status));
1297141cc406Sopenharmony_ci	if (!rc) goto the_end;
1298141cc406Sopenharmony_ci
1299141cc406Sopenharmony_ci	test_parameters(device, &params);
1300141cc406Sopenharmony_ci
1301141cc406Sopenharmony_ci	if (params.bytes_per_line != 0 && params.lines != 0) {
1302141cc406Sopenharmony_ci
1303141cc406Sopenharmony_ci		len = 10;
1304141cc406Sopenharmony_ci
1305141cc406Sopenharmony_ci		guards_set(image, 1);
1306141cc406Sopenharmony_ci		status = sane_read (device, image, 1, &len);
1307141cc406Sopenharmony_ci		guards_check(image, 1);
1308141cc406Sopenharmony_ci
1309141cc406Sopenharmony_ci		check(ERR, (len == 1),
1310141cc406Sopenharmony_ci			  "sane_read() didn't return 1 byte as requested");
1311141cc406Sopenharmony_ci	}
1312141cc406Sopenharmony_ci
1313141cc406Sopenharmony_ci	sane_cancel(device);
1314141cc406Sopenharmony_ci
1315141cc406Sopenharmony_ci
1316141cc406Sopenharmony_ci	/*
1317141cc406Sopenharmony_ci	 * Do a scan, reading random length.
1318141cc406Sopenharmony_ci	 */
1319141cc406Sopenharmony_ci	check(MSG, 0, "TEST: scan random length - %s", display_scan_parameters(device));
1320141cc406Sopenharmony_ci
1321141cc406Sopenharmony_ci	test_parameters(device, &params);
1322141cc406Sopenharmony_ci
1323141cc406Sopenharmony_ci	/* Try a read outside a scan. */
1324141cc406Sopenharmony_ci	ask_len = 20;
1325141cc406Sopenharmony_ci	guards_set(image, ask_len);
1326141cc406Sopenharmony_ci	status = sane_read (device, image, ask_len, &len);
1327141cc406Sopenharmony_ci	guards_check(image, ask_len);
1328141cc406Sopenharmony_ci	check(ERR, (status != SANE_STATUS_GOOD),
1329141cc406Sopenharmony_ci		  "it is possible to sane_read outside a scan");
1330141cc406Sopenharmony_ci
1331141cc406Sopenharmony_ci	status = sane_start (device);
1332141cc406Sopenharmony_ci	rc = check(ERR, (status == SANE_STATUS_GOOD),
1333141cc406Sopenharmony_ci			   "cannot start the scan (%s)", sane_strstatus (status));
1334141cc406Sopenharmony_ci	if (!rc) goto the_end;
1335141cc406Sopenharmony_ci
1336141cc406Sopenharmony_ci	/* Check that it is not possible to set an option. */
1337141cc406Sopenharmony_ci	status = sane_control_option (device, option_num,
1338141cc406Sopenharmony_ci								  SANE_ACTION_SET_VALUE,
1339141cc406Sopenharmony_ci								  &val_int , NULL);
1340141cc406Sopenharmony_ci	check(WRN, (status != SANE_STATUS_GOOD),
1341141cc406Sopenharmony_ci		  "it is possible to set a value during a scan");
1342141cc406Sopenharmony_ci
1343141cc406Sopenharmony_ci	test_parameters(device, &params);
1344141cc406Sopenharmony_ci
1345141cc406Sopenharmony_ci	if (params.bytes_per_line != 0 && params.lines != 0) {
1346141cc406Sopenharmony_ci
1347141cc406Sopenharmony_ci		to_read = params.bytes_per_line * params.lines;
1348141cc406Sopenharmony_ci		srandom(time(NULL));
1349141cc406Sopenharmony_ci
1350141cc406Sopenharmony_ci		while (SANE_TRUE) {
1351141cc406Sopenharmony_ci
1352141cc406Sopenharmony_ci			ask_len = rand() & 0x7ffff;	/* 0 to 512K-1 */
1353141cc406Sopenharmony_ci			if (ask_len == 0) len = 1;
1354141cc406Sopenharmony_ci			len = ask_len + 4978; /* garbage */
1355141cc406Sopenharmony_ci
1356141cc406Sopenharmony_ci			guards_set(image, ask_len);
1357141cc406Sopenharmony_ci			status = sane_read (device, image, ask_len, &len);
1358141cc406Sopenharmony_ci			guards_check(image, ask_len);
1359141cc406Sopenharmony_ci
1360141cc406Sopenharmony_ci			if (status == SANE_STATUS_EOF) {
1361141cc406Sopenharmony_ci				/* End of scan */
1362141cc406Sopenharmony_ci				check(ERR, (len == 0),
1363141cc406Sopenharmony_ci					  "the length returned is not 0");
1364141cc406Sopenharmony_ci				break;
1365141cc406Sopenharmony_ci			}
1366141cc406Sopenharmony_ci
1367141cc406Sopenharmony_ci			rc = check(ERR, (status == SANE_STATUS_GOOD),
1368141cc406Sopenharmony_ci					   "scan stopped - status is %s", sane_strstatus (status));
1369141cc406Sopenharmony_ci			if (!rc) {
1370141cc406Sopenharmony_ci				check(ERR, (len == 0),
1371141cc406Sopenharmony_ci					  "the length returned is not 0");
1372141cc406Sopenharmony_ci				break;
1373141cc406Sopenharmony_ci			}
1374141cc406Sopenharmony_ci
1375141cc406Sopenharmony_ci			/* The scanner cannot return 0. If it returns 0, we may
1376141cc406Sopenharmony_ci			 * loop forever. */
1377141cc406Sopenharmony_ci			rc = check(ERR, (len > 0),
1378141cc406Sopenharmony_ci					   "backend didn't return any data - skipping test");
1379141cc406Sopenharmony_ci			if (!rc) {
1380141cc406Sopenharmony_ci				break;
1381141cc406Sopenharmony_ci			}
1382141cc406Sopenharmony_ci			rc = check(ERR, (len <= ask_len),
1383141cc406Sopenharmony_ci					   "backend returned too much data (%d / %d) - skipping test",
1384141cc406Sopenharmony_ci					   len, ask_len);
1385141cc406Sopenharmony_ci			if (!rc) {
1386141cc406Sopenharmony_ci				break;
1387141cc406Sopenharmony_ci			}
1388141cc406Sopenharmony_ci
1389141cc406Sopenharmony_ci			to_read -= len;
1390141cc406Sopenharmony_ci		}
1391141cc406Sopenharmony_ci
1392141cc406Sopenharmony_ci		if (params.lines != -1) {
1393141cc406Sopenharmony_ci			check(ERR, (to_read == 0),
1394141cc406Sopenharmony_ci				  "scan ended, but data was truncated");
1395141cc406Sopenharmony_ci		}
1396141cc406Sopenharmony_ci	}
1397141cc406Sopenharmony_ci
1398141cc406Sopenharmony_ci	sane_cancel(device);
1399141cc406Sopenharmony_ci
1400141cc406Sopenharmony_ci	/* Try a read outside a scan. */
1401141cc406Sopenharmony_ci	ask_len = 30;
1402141cc406Sopenharmony_ci	guards_set(image, ask_len);
1403141cc406Sopenharmony_ci	status = sane_read (device, image, ask_len, &len);
1404141cc406Sopenharmony_ci	guards_check(image, ask_len);
1405141cc406Sopenharmony_ci	check(ERR, (status != SANE_STATUS_GOOD),
1406141cc406Sopenharmony_ci		  "it is possible to sane_read outside a scan");
1407141cc406Sopenharmony_ci
1408141cc406Sopenharmony_ci	/*
1409141cc406Sopenharmony_ci	 * Do a scan with a fixed size and a big buffer
1410141cc406Sopenharmony_ci	 */
1411141cc406Sopenharmony_ci	check(MSG, 0, "TEST: scan with a big max_len - %s", display_scan_parameters(device));
1412141cc406Sopenharmony_ci
1413141cc406Sopenharmony_ci	test_parameters(device, &params);
1414141cc406Sopenharmony_ci
1415141cc406Sopenharmony_ci	status = sane_start (device);
1416141cc406Sopenharmony_ci	rc = check(ERR, (status == SANE_STATUS_GOOD),
1417141cc406Sopenharmony_ci			   "cannot start the scan (%s)", sane_strstatus (status));
1418141cc406Sopenharmony_ci	if (!rc) goto the_end;
1419141cc406Sopenharmony_ci
1420141cc406Sopenharmony_ci	test_parameters(device, &params);
1421141cc406Sopenharmony_ci
1422141cc406Sopenharmony_ci	if (params.bytes_per_line != 0 && params.lines != 0) {
1423141cc406Sopenharmony_ci
1424141cc406Sopenharmony_ci		to_read = params.bytes_per_line * params.lines;
1425141cc406Sopenharmony_ci		while(SANE_TRUE) {
1426141cc406Sopenharmony_ci			ask_len = IMAGE_SIZE;
1427141cc406Sopenharmony_ci			len = rand();		/* garbage */
1428141cc406Sopenharmony_ci
1429141cc406Sopenharmony_ci			guards_set(image, ask_len);
1430141cc406Sopenharmony_ci			status = sane_read (device, image, ask_len, &len);
1431141cc406Sopenharmony_ci			guards_check(image, ask_len);
1432141cc406Sopenharmony_ci
1433141cc406Sopenharmony_ci			if (status == SANE_STATUS_EOF) {
1434141cc406Sopenharmony_ci				/* End of scan */
1435141cc406Sopenharmony_ci				check(ERR, (len == 0),
1436141cc406Sopenharmony_ci					  "the length returned is not 0");
1437141cc406Sopenharmony_ci				break;
1438141cc406Sopenharmony_ci			}
1439141cc406Sopenharmony_ci
1440141cc406Sopenharmony_ci			rc = check(ERR, (status == SANE_STATUS_GOOD),
1441141cc406Sopenharmony_ci					   "scan stopped - status is %s", sane_strstatus (status));
1442141cc406Sopenharmony_ci			if (!rc) {
1443141cc406Sopenharmony_ci				check(ERR, (len == 0),
1444141cc406Sopenharmony_ci					  "the length returned is not 0");
1445141cc406Sopenharmony_ci				break;
1446141cc406Sopenharmony_ci			}
1447141cc406Sopenharmony_ci
1448141cc406Sopenharmony_ci			/* If the scanner return 0, we may loop forever. */
1449141cc406Sopenharmony_ci			rc = check(ERR, (len > 0),
1450141cc406Sopenharmony_ci					   "backend didn't return any data - skipping test");
1451141cc406Sopenharmony_ci			if (!rc) {
1452141cc406Sopenharmony_ci				break;
1453141cc406Sopenharmony_ci			}
1454141cc406Sopenharmony_ci
1455141cc406Sopenharmony_ci			rc = check(ERR, (len <= ask_len),
1456141cc406Sopenharmony_ci					   "backend returned too much data (%d / %d) - skipping test",
1457141cc406Sopenharmony_ci					   len, ask_len);
1458141cc406Sopenharmony_ci			if (!rc) {
1459141cc406Sopenharmony_ci				break;
1460141cc406Sopenharmony_ci			}
1461141cc406Sopenharmony_ci
1462141cc406Sopenharmony_ci			to_read -= len;
1463141cc406Sopenharmony_ci		}
1464141cc406Sopenharmony_ci
1465141cc406Sopenharmony_ci		if (params.lines != -1) {
1466141cc406Sopenharmony_ci			check(ERR, (to_read == 0),
1467141cc406Sopenharmony_ci				  "scan ended, but data was truncated");
1468141cc406Sopenharmony_ci		}
1469141cc406Sopenharmony_ci	}
1470141cc406Sopenharmony_ci
1471141cc406Sopenharmony_ci	sane_cancel(device);
1472141cc406Sopenharmony_ci
1473141cc406Sopenharmony_ci the_end:
1474141cc406Sopenharmony_ci	if (image) guards_free(image);
1475141cc406Sopenharmony_ci}
1476141cc406Sopenharmony_ci
1477141cc406Sopenharmony_ci/* Do several scans at different scan mode and resolution. */
1478141cc406Sopenharmony_cistatic void test_scans(SANE_Device * device)
1479141cc406Sopenharmony_ci{
1480141cc406Sopenharmony_ci	const SANE_Option_Descriptor *scan_mode_opt;
1481141cc406Sopenharmony_ci	const SANE_Option_Descriptor *resolution_mode_opt;
1482141cc406Sopenharmony_ci	SANE_Status status;
1483141cc406Sopenharmony_ci	int scan_mode_optnum;
1484141cc406Sopenharmony_ci	int resolution_mode_optnum;
1485141cc406Sopenharmony_ci	SANE_String val_string;
1486141cc406Sopenharmony_ci	int i;
1487141cc406Sopenharmony_ci	int rc;
1488141cc406Sopenharmony_ci
1489141cc406Sopenharmony_ci	/* For that test, the requirements are:
1490141cc406Sopenharmony_ci	 *   SANE_NAME_SCAN_MODE exists and is a SANE_CONSTRAINT_STRING_LIST
1491141cc406Sopenharmony_ci	 *   SANE_NAME_SCAN_RESOLUTION exists and is either a SANE_CONSTRAINT_WORD_LIST or a SANE_CONSTRAINT_RANGE.
1492141cc406Sopenharmony_ci	 *
1493141cc406Sopenharmony_ci	 * These are not a SANE requirement, though.
1494141cc406Sopenharmony_ci	 */
1495141cc406Sopenharmony_ci
1496141cc406Sopenharmony_ci	scan_mode_opt = get_optdesc_by_name(device, SANE_NAME_SCAN_MODE, &scan_mode_optnum);
1497141cc406Sopenharmony_ci	if (scan_mode_opt) {
1498141cc406Sopenharmony_ci
1499141cc406Sopenharmony_ci		rc = check(INF, (scan_mode_opt->type == SANE_TYPE_STRING),
1500141cc406Sopenharmony_ci				   "option [%s] is not a SANE_TYPE_STRING - skipping test", SANE_NAME_SCAN_MODE);
1501141cc406Sopenharmony_ci		if (!rc) return;
1502141cc406Sopenharmony_ci		rc = check(INF, (scan_mode_opt->constraint_type == SANE_CONSTRAINT_STRING_LIST),
1503141cc406Sopenharmony_ci				   "constraint for option [%s] is not SANE_CONSTRAINT_STRING_LIST - skipping test", SANE_NAME_SCAN_MODE);
1504141cc406Sopenharmony_ci		if (!rc) return;
1505141cc406Sopenharmony_ci		rc = check(INF, (SANE_OPTION_IS_SETTABLE(scan_mode_opt->cap)),
1506141cc406Sopenharmony_ci				   "option [%s] is not settable - skipping test", SANE_NAME_SCAN_MODE);
1507141cc406Sopenharmony_ci		if (!rc) return;
1508141cc406Sopenharmony_ci	}
1509141cc406Sopenharmony_ci
1510141cc406Sopenharmony_ci	resolution_mode_opt = get_optdesc_by_name(device, SANE_NAME_SCAN_RESOLUTION, &resolution_mode_optnum);
1511141cc406Sopenharmony_ci	if (resolution_mode_opt) {
1512141cc406Sopenharmony_ci		rc = check(INF, (SANE_OPTION_IS_SETTABLE(resolution_mode_opt->cap)),
1513141cc406Sopenharmony_ci				   "option [%s] is not settable - skipping test", SANE_NAME_SCAN_RESOLUTION);
1514141cc406Sopenharmony_ci		if (!rc) return;
1515141cc406Sopenharmony_ci	}
1516141cc406Sopenharmony_ci
1517141cc406Sopenharmony_ci	if (scan_mode_opt) {
1518141cc406Sopenharmony_ci		/* Do several scans, with several resolution. */
1519141cc406Sopenharmony_ci		for (i=0; scan_mode_opt->constraint.string_list[i] != NULL; i++) {
1520141cc406Sopenharmony_ci
1521141cc406Sopenharmony_ci			val_string = strdup(scan_mode_opt->constraint.string_list[i]);
1522141cc406Sopenharmony_ci			assert(val_string);
1523141cc406Sopenharmony_ci
1524141cc406Sopenharmony_ci			status = sane_control_option (device, scan_mode_optnum,
1525141cc406Sopenharmony_ci										  SANE_ACTION_SET_VALUE, val_string, NULL);
1526141cc406Sopenharmony_ci			check(FATAL, (status == SANE_STATUS_GOOD),
1527141cc406Sopenharmony_ci				  "cannot set a settable option (status=%s)", sane_strstatus(status));
1528141cc406Sopenharmony_ci
1529141cc406Sopenharmony_ci			free(val_string);
1530141cc406Sopenharmony_ci
1531141cc406Sopenharmony_ci			if (resolution_mode_opt) {
1532141cc406Sopenharmony_ci				set_min_value(device, resolution_mode_optnum,
1533141cc406Sopenharmony_ci							  resolution_mode_opt);
1534141cc406Sopenharmony_ci				test_scan(device);
1535141cc406Sopenharmony_ci
1536141cc406Sopenharmony_ci				set_max_value(device, resolution_mode_optnum,
1537141cc406Sopenharmony_ci							  resolution_mode_opt);
1538141cc406Sopenharmony_ci				test_scan(device);
1539141cc406Sopenharmony_ci
1540141cc406Sopenharmony_ci				set_random_value(device, resolution_mode_optnum,
1541141cc406Sopenharmony_ci								 resolution_mode_opt);
1542141cc406Sopenharmony_ci				test_scan(device);
1543141cc406Sopenharmony_ci			} else {
1544141cc406Sopenharmony_ci				test_scan(device);
1545141cc406Sopenharmony_ci			}
1546141cc406Sopenharmony_ci		}
1547141cc406Sopenharmony_ci	} else {
1548141cc406Sopenharmony_ci		if (resolution_mode_opt) {
1549141cc406Sopenharmony_ci			set_min_value(device, resolution_mode_optnum,
1550141cc406Sopenharmony_ci						  resolution_mode_opt);
1551141cc406Sopenharmony_ci			test_scan(device);
1552141cc406Sopenharmony_ci
1553141cc406Sopenharmony_ci			set_max_value(device, resolution_mode_optnum,
1554141cc406Sopenharmony_ci						  resolution_mode_opt);
1555141cc406Sopenharmony_ci			test_scan(device);
1556141cc406Sopenharmony_ci
1557141cc406Sopenharmony_ci			set_random_value(device, resolution_mode_optnum,
1558141cc406Sopenharmony_ci							 resolution_mode_opt);
1559141cc406Sopenharmony_ci			test_scan(device);
1560141cc406Sopenharmony_ci		} else {
1561141cc406Sopenharmony_ci			test_scan(device);
1562141cc406Sopenharmony_ci		}
1563141cc406Sopenharmony_ci	}
1564141cc406Sopenharmony_ci}
1565141cc406Sopenharmony_ci
1566141cc406Sopenharmony_ci/** test sane_get_devices
1567141cc406Sopenharmony_ci * test sane_get_device function, if time is greter than 0,
1568141cc406Sopenharmony_ci * loop to let tester plug/unplug device to check for correct
1569141cc406Sopenharmony_ci * hotplug detection
1570141cc406Sopenharmony_ci * @param device_list device list to fill
1571141cc406Sopenharmony_ci * @param time time to loop
1572141cc406Sopenharmony_ci * @return 0 on success
1573141cc406Sopenharmony_ci */
1574141cc406Sopenharmony_cistatic int test_get_devices(const SANE_Device ***device_list, int time)
1575141cc406Sopenharmony_ci{
1576141cc406Sopenharmony_ciint loop=0;
1577141cc406Sopenharmony_ciint i;
1578141cc406Sopenharmony_ciconst SANE_Device *dev;
1579141cc406Sopenharmony_ciSANE_Status status;
1580141cc406Sopenharmony_ci
1581141cc406Sopenharmony_ci	status = sane_get_devices (device_list, SANE_TRUE);
1582141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
1583141cc406Sopenharmony_ci		  "sane_get_devices() failed (%s)", sane_strstatus (status));
1584141cc406Sopenharmony_ci
1585141cc406Sopenharmony_ci	/* Verify that the SANE doc (or tstbackend) is up to date */
1586141cc406Sopenharmony_ci	for (i=0; (*device_list)[i] != NULL; i++) {
1587141cc406Sopenharmony_ci
1588141cc406Sopenharmony_ci		dev = (*device_list)[i];
1589141cc406Sopenharmony_ci
1590141cc406Sopenharmony_ci		check(FATAL, (dev->name != NULL), "device name is NULL");
1591141cc406Sopenharmony_ci		check(FATAL, (dev->vendor != NULL), "device vendor is NULL");
1592141cc406Sopenharmony_ci		check(FATAL, (dev->type != NULL), "device type is NULL");
1593141cc406Sopenharmony_ci		check(FATAL, (dev->model != NULL), "device model is NULL");
1594141cc406Sopenharmony_ci
1595141cc406Sopenharmony_ci		check(INF, ((strcmp(dev->type, "flatbed scanner") == 0) ||
1596141cc406Sopenharmony_ci					(strcmp(dev->type, "frame grabber") == 0) ||
1597141cc406Sopenharmony_ci					(strcmp(dev->type, "handheld scanner") == 0) ||
1598141cc406Sopenharmony_ci					(strcmp(dev->type, "still camera") == 0) ||
1599141cc406Sopenharmony_ci					(strcmp(dev->type, "video camera") == 0) ||
1600141cc406Sopenharmony_ci					(strcmp(dev->type, "virtual device") == 0) ||
1601141cc406Sopenharmony_ci					(strcmp(dev->type, "film scanner") == 0) ||
1602141cc406Sopenharmony_ci					(strcmp(dev->type, "multi-function peripheral") == 0) ||
1603141cc406Sopenharmony_ci					(strcmp(dev->type, "sheetfed scanner") == 0)),
1604141cc406Sopenharmony_ci					"unknown device type [%s]. Update SANE doc section \"Type Strings\"", dev->type);
1605141cc406Sopenharmony_ci
1606141cc406Sopenharmony_ci		check(INF, (
1607141cc406Sopenharmony_ci					(strcmp(dev->vendor, "AGFA") == 0) ||
1608141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Abaton") == 0) ||
1609141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Acer") == 0) ||
1610141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Apple") == 0) ||
1611141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Artec") == 0) ||
1612141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Avision") == 0) ||
1613141cc406Sopenharmony_ci					(strcmp(dev->vendor, "CANON") == 0) ||
1614141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Connectix") == 0) ||
1615141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Epson") == 0) ||
1616141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Fujitsu") == 0) ||
1617141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Gphoto2") == 0) ||
1618141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Hewlett-Packard") == 0) ||
1619141cc406Sopenharmony_ci					(strcmp(dev->vendor, "IBM") == 0) ||
1620141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Kodak") == 0) ||
1621141cc406Sopenharmony_ci                                        (strcmp(dev->vendor, "Lexmark") == 0) ||
1622141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Logitech") == 0) ||
1623141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Microtek") == 0) ||
1624141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Minolta") == 0) ||
1625141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Mitsubishi") == 0) ||
1626141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Mustek") == 0) ||
1627141cc406Sopenharmony_ci					(strcmp(dev->vendor, "NEC") == 0) ||
1628141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Nikon") == 0) ||
1629141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Noname") == 0) ||
1630141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Plustek") == 0) ||
1631141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Polaroid") == 0) ||
1632141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Relisys") == 0) ||
1633141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Ricoh") == 0) ||
1634141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Sharp") == 0) ||
1635141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Siemens") == 0) ||
1636141cc406Sopenharmony_ci					(strcmp(dev->vendor, "Tamarack") == 0) ||
1637141cc406Sopenharmony_ci					(strcmp(dev->vendor, "UMAX") == 0)),
1638141cc406Sopenharmony_ci			  "unknown device vendor [%s]. Update SANE doc section \"Vendor Strings\"", dev->vendor);
1639141cc406Sopenharmony_ci	}
1640141cc406Sopenharmony_ci
1641141cc406Sopenharmony_ci	/* loop on detecting device to let time to plug/unplug scanners */
1642141cc406Sopenharmony_ci	while(loop<time) {
1643141cc406Sopenharmony_ci		/* print and free detected device list */
1644141cc406Sopenharmony_ci		check(MSG, 0, "DETECTED DEVICES:");
1645141cc406Sopenharmony_ci		for (i=0; (*device_list)[i] != NULL; i++) {
1646141cc406Sopenharmony_ci			dev = (*device_list)[i];
1647141cc406Sopenharmony_ci			check(MSG, 0, "\t%s:%s %s:%s", dev->vendor, dev->name, dev->type, dev->model);
1648141cc406Sopenharmony_ci		}
1649141cc406Sopenharmony_ci		if(i==0) {
1650141cc406Sopenharmony_ci			check(MSG, 0, "\tnone...");
1651141cc406Sopenharmony_ci		}
1652141cc406Sopenharmony_ci		sleep(1);
1653141cc406Sopenharmony_ci		(*device_list) = NULL;
1654141cc406Sopenharmony_ci		status = sane_get_devices (device_list, SANE_TRUE);
1655141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1656141cc406Sopenharmony_ci		  "sane_get_devices() failed (%s)", sane_strstatus (status));
1657141cc406Sopenharmony_ci		loop++;
1658141cc406Sopenharmony_ci	}
1659141cc406Sopenharmony_ci	return 0;
1660141cc406Sopenharmony_ci}
1661141cc406Sopenharmony_ci
1662141cc406Sopenharmony_ci/** test test_default
1663141cc406Sopenharmony_ci * test by scanning using default values
1664141cc406Sopenharmony_ci * @param device device to use for the scan
1665141cc406Sopenharmony_ci */
1666141cc406Sopenharmony_cistatic void test_default(SANE_Device * device)
1667141cc406Sopenharmony_ci{
1668141cc406Sopenharmony_ci	test_scan(device);
1669141cc406Sopenharmony_ci}
1670141cc406Sopenharmony_ci
1671141cc406Sopenharmony_cistatic void usage(const char *execname)
1672141cc406Sopenharmony_ci{
1673141cc406Sopenharmony_ci	printf("Usage: %s [-d backend_name] [-l test_level] [-s] [-r recursion_level] [-g time (s)]\n", execname);
1674141cc406Sopenharmony_ci	printf("\t-v\tverbose level\n");
1675141cc406Sopenharmony_ci	printf("\t-d\tbackend name\n");
1676141cc406Sopenharmony_ci	printf("\t-l\tlevel of testing (0=some, 1=0+options, 2=1+scans, 3=longest tests)\n");
1677141cc406Sopenharmony_ci	printf("\t-s\tdo a scan during open/close tests\n");
1678141cc406Sopenharmony_ci	printf("\t-r\trecursion level for option testing (the higher, the longer)\n");
1679141cc406Sopenharmony_ci	printf("\t-g\ttime to loop on sane_get_devices function to test scannet hotplug detection (time is in seconds).\n");
1680141cc406Sopenharmony_ci}
1681141cc406Sopenharmony_ci
1682141cc406Sopenharmony_ciint
1683141cc406Sopenharmony_cimain (int argc, char **argv)
1684141cc406Sopenharmony_ci{
1685141cc406Sopenharmony_ci	char *devname = NULL;
1686141cc406Sopenharmony_ci	SANE_Status status;
1687141cc406Sopenharmony_ci	SANE_Int version_code;
1688141cc406Sopenharmony_ci	SANE_Handle device;
1689141cc406Sopenharmony_ci	int ch;
1690141cc406Sopenharmony_ci	int index;
1691141cc406Sopenharmony_ci	int i;
1692141cc406Sopenharmony_ci	const SANE_Device **device_list;
1693141cc406Sopenharmony_ci	int rc;
1694141cc406Sopenharmony_ci	int recursion_level;
1695141cc406Sopenharmony_ci	int time;
1696141cc406Sopenharmony_ci	int default_scan;
1697141cc406Sopenharmony_ci
1698141cc406Sopenharmony_ci	printf("tstbackend, Copyright (C) 2002 Frank Zago\n");
1699141cc406Sopenharmony_ci	printf("tstbackend comes with ABSOLUTELY NO WARRANTY\n");
1700141cc406Sopenharmony_ci	printf("This is free software, and you are welcome to redistribute it\n");
1701141cc406Sopenharmony_ci	printf("under certain conditions. See COPYING file for details\n\n");
1702141cc406Sopenharmony_ci	printf("This is tstbackend build %d\n\n", BUILD);
1703141cc406Sopenharmony_ci
1704141cc406Sopenharmony_ci	/* Read the command line options. */
1705141cc406Sopenharmony_ci	opterr = 0;
1706141cc406Sopenharmony_ci	recursion_level = 5;		/* 5 levels or recursion should be enough */
1707141cc406Sopenharmony_ci	test_level = 0;			/* basic tests only */
1708141cc406Sopenharmony_ci	time = 0;			/* no get devices loop */
1709141cc406Sopenharmony_ci	default_scan = 0;
1710141cc406Sopenharmony_ci
1711141cc406Sopenharmony_ci	while ((ch = getopt_long (argc, argv, "-v:d:l:r:g:h:s", basic_options,
1712141cc406Sopenharmony_ci							  &index)) != EOF) {
1713141cc406Sopenharmony_ci		switch(ch) {
1714141cc406Sopenharmony_ci		case 'v':
1715141cc406Sopenharmony_ci			verbose_level = atoi(optarg);
1716141cc406Sopenharmony_ci			break;
1717141cc406Sopenharmony_ci
1718141cc406Sopenharmony_ci		case 'd':
1719141cc406Sopenharmony_ci			devname = strdup(optarg);
1720141cc406Sopenharmony_ci			break;
1721141cc406Sopenharmony_ci
1722141cc406Sopenharmony_ci		case 'l':
1723141cc406Sopenharmony_ci			test_level = atoi(optarg);
1724141cc406Sopenharmony_ci			if (test_level < 0 || test_level > 4) {
1725141cc406Sopenharmony_ci				fprintf(stderr, "invalid test_level\n");
1726141cc406Sopenharmony_ci				return(1);
1727141cc406Sopenharmony_ci			}
1728141cc406Sopenharmony_ci			break;
1729141cc406Sopenharmony_ci
1730141cc406Sopenharmony_ci		case 's':
1731141cc406Sopenharmony_ci			default_scan = 1;
1732141cc406Sopenharmony_ci			break;
1733141cc406Sopenharmony_ci
1734141cc406Sopenharmony_ci		case 'r':
1735141cc406Sopenharmony_ci			recursion_level = atoi(optarg);
1736141cc406Sopenharmony_ci			break;
1737141cc406Sopenharmony_ci
1738141cc406Sopenharmony_ci		case 'g':
1739141cc406Sopenharmony_ci			time = atoi(optarg);
1740141cc406Sopenharmony_ci			break;
1741141cc406Sopenharmony_ci
1742141cc406Sopenharmony_ci		case 'h':
1743141cc406Sopenharmony_ci			usage(argv[0]);
1744141cc406Sopenharmony_ci			return(0);
1745141cc406Sopenharmony_ci
1746141cc406Sopenharmony_ci		case '?':
1747141cc406Sopenharmony_ci			fprintf(stderr, "invalid option\n");
1748141cc406Sopenharmony_ci			return(1);
1749141cc406Sopenharmony_ci
1750141cc406Sopenharmony_ci		default:
1751141cc406Sopenharmony_ci			fprintf(stderr, "bug in tstbackend\n");
1752141cc406Sopenharmony_ci			return(1);
1753141cc406Sopenharmony_ci		}
1754141cc406Sopenharmony_ci	}
1755141cc406Sopenharmony_ci
1756141cc406Sopenharmony_ci	/* First test */
1757141cc406Sopenharmony_ci	check(MSG, 0, "TEST: init/exit");
1758141cc406Sopenharmony_ci	for (i=0; i<10; i++) {
1759141cc406Sopenharmony_ci		/* Test 1. init/exit with a version code */
1760141cc406Sopenharmony_ci		status = sane_init(&version_code, NULL);
1761141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1762141cc406Sopenharmony_ci			  "sane_init failed with %s", sane_strstatus (status));
1763141cc406Sopenharmony_ci		check(FATAL, (SANE_VERSION_MAJOR(version_code) == 1),
1764141cc406Sopenharmony_ci			  "invalid SANE version linked");
1765141cc406Sopenharmony_ci		sane_exit();
1766141cc406Sopenharmony_ci
1767141cc406Sopenharmony_ci		/* Test 2. init/exit without a version code */
1768141cc406Sopenharmony_ci		status = sane_init(NULL, NULL);
1769141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1770141cc406Sopenharmony_ci			  "sane_init failed with %s", sane_strstatus (status));
1771141cc406Sopenharmony_ci		sane_exit();
1772141cc406Sopenharmony_ci
1773141cc406Sopenharmony_ci		/* Test 3. Init/get_devices/open invalid/exit */
1774141cc406Sopenharmony_ci		status = sane_init(NULL, NULL);
1775141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1776141cc406Sopenharmony_ci			  "sane_init failed with %s", sane_strstatus (status));
1777141cc406Sopenharmony_ci
1778141cc406Sopenharmony_ci		status = sane_get_devices (&device_list, SANE_TRUE);
1779141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1780141cc406Sopenharmony_ci			  "sane_get_devices() failed (%s)", sane_strstatus (status));
1781141cc406Sopenharmony_ci
1782141cc406Sopenharmony_ci		status = sane_open ("opihndvses75bvt6fg", &device);
1783141cc406Sopenharmony_ci		check(WRN, (status == SANE_STATUS_INVAL),
1784141cc406Sopenharmony_ci			  "sane_open() failed (%s)", sane_strstatus (status));
1785141cc406Sopenharmony_ci
1786141cc406Sopenharmony_ci		if (status == SANE_STATUS_GOOD)
1787141cc406Sopenharmony_ci			sane_close(device);
1788141cc406Sopenharmony_ci
1789141cc406Sopenharmony_ci		sane_exit();
1790141cc406Sopenharmony_ci
1791141cc406Sopenharmony_ci		/* Test 4. Init/get_devices/open default/exit */
1792141cc406Sopenharmony_ci		status = sane_init(NULL, NULL);
1793141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1794141cc406Sopenharmony_ci			  "sane_init failed with %s", sane_strstatus (status));
1795141cc406Sopenharmony_ci
1796141cc406Sopenharmony_ci		status = sane_get_devices (&device_list, SANE_TRUE);
1797141cc406Sopenharmony_ci		check(FATAL, (status == SANE_STATUS_GOOD),
1798141cc406Sopenharmony_ci			  "sane_get_devices() failed (%s)", sane_strstatus (status));
1799141cc406Sopenharmony_ci
1800141cc406Sopenharmony_ci		status = sane_open ("", &device);
1801141cc406Sopenharmony_ci		if (status == SANE_STATUS_GOOD)
1802141cc406Sopenharmony_ci			sane_close(device);
1803141cc406Sopenharmony_ci
1804141cc406Sopenharmony_ci		sane_exit();
1805141cc406Sopenharmony_ci	}
1806141cc406Sopenharmony_ci
1807141cc406Sopenharmony_ci	status = sane_init (&version_code, NULL);
1808141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
1809141cc406Sopenharmony_ci		  "sane_init failed with %s", sane_strstatus (status));
1810141cc406Sopenharmony_ci
1811141cc406Sopenharmony_ci	/* Check the device list */
1812141cc406Sopenharmony_ci	rc = test_get_devices(&device_list, time);
1813141cc406Sopenharmony_ci	if (rc) goto the_exit;
1814141cc406Sopenharmony_ci
1815141cc406Sopenharmony_ci	if (!devname) {
1816141cc406Sopenharmony_ci		/* If no device name was specified explicitly, we look at the
1817141cc406Sopenharmony_ci		   environment variable SANE_DEFAULT_DEVICE.  If this variable
1818141cc406Sopenharmony_ci		   is not set, we open the first device we find (if any): */
1819141cc406Sopenharmony_ci		devname = getenv ("SANE_DEFAULT_DEVICE");
1820141cc406Sopenharmony_ci		if (devname) devname = strdup(devname);
1821141cc406Sopenharmony_ci	}
1822141cc406Sopenharmony_ci
1823141cc406Sopenharmony_ci	if (!devname) {
1824141cc406Sopenharmony_ci		if (device_list[0]) {
1825141cc406Sopenharmony_ci			devname = strdup(device_list[0]->name);
1826141cc406Sopenharmony_ci		}
1827141cc406Sopenharmony_ci	}
1828141cc406Sopenharmony_ci
1829141cc406Sopenharmony_ci	rc = check(ERR, (devname != NULL),
1830141cc406Sopenharmony_ci			   "no SANE devices found");
1831141cc406Sopenharmony_ci	if (!rc) goto the_exit;
1832141cc406Sopenharmony_ci
1833141cc406Sopenharmony_ci	check(MSG, 0, "using device %s", devname);
1834141cc406Sopenharmony_ci
1835141cc406Sopenharmony_ci	/* Test open close */
1836141cc406Sopenharmony_ci	check(MSG, 0, "TEST: open/close");
1837141cc406Sopenharmony_ci	for (i=0; i<10; i++) {
1838141cc406Sopenharmony_ci		status = sane_open (devname, &device);
1839141cc406Sopenharmony_ci		rc = check(ERR, (status == SANE_STATUS_GOOD),
1840141cc406Sopenharmony_ci				   "sane_open failed with %s for device %s", sane_strstatus (status), devname);
1841141cc406Sopenharmony_ci		if (!rc) goto the_exit;
1842141cc406Sopenharmony_ci
1843141cc406Sopenharmony_ci		if (default_scan) {
1844141cc406Sopenharmony_ci			test_default (device);
1845141cc406Sopenharmony_ci		}
1846141cc406Sopenharmony_ci		sane_close (device);
1847141cc406Sopenharmony_ci	}
1848141cc406Sopenharmony_ci
1849141cc406Sopenharmony_ci	if (test_level < 1) {
1850141cc406Sopenharmony_ci		sane_exit();
1851141cc406Sopenharmony_ci		goto the_exit;
1852141cc406Sopenharmony_ci	}
1853141cc406Sopenharmony_ci
1854141cc406Sopenharmony_ci
1855141cc406Sopenharmony_ci	/* Test options */
1856141cc406Sopenharmony_ci	check(MSG, 0, "TEST: options consistency");
1857141cc406Sopenharmony_ci	status = sane_open (devname, &device);
1858141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
1859141cc406Sopenharmony_ci		  "sane_open failed with %s for device %s", sane_strstatus (status), devname);
1860141cc406Sopenharmony_ci
1861141cc406Sopenharmony_ci	test_parameters(device, NULL);
1862141cc406Sopenharmony_ci	test_options(device, recursion_level);
1863141cc406Sopenharmony_ci	sane_close (device);
1864141cc406Sopenharmony_ci	sane_exit();
1865141cc406Sopenharmony_ci
1866141cc406Sopenharmony_ci	if (test_level < 2) {
1867141cc406Sopenharmony_ci		goto the_exit;
1868141cc406Sopenharmony_ci	}
1869141cc406Sopenharmony_ci
1870141cc406Sopenharmony_ci
1871141cc406Sopenharmony_ci	/* Test scans */
1872141cc406Sopenharmony_ci	check(MSG, 0, "TEST: scan test");
1873141cc406Sopenharmony_ci	status = sane_init (&version_code, NULL);
1874141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
1875141cc406Sopenharmony_ci		  "sane_init failed with %s", sane_strstatus (status));
1876141cc406Sopenharmony_ci	status = sane_open (devname, &device);
1877141cc406Sopenharmony_ci	check(FATAL, (status == SANE_STATUS_GOOD),
1878141cc406Sopenharmony_ci		  "sane_open failed with %s for device %s", sane_strstatus (status), devname);
1879141cc406Sopenharmony_ci	test_scans(device);
1880141cc406Sopenharmony_ci	sane_close (device);
1881141cc406Sopenharmony_ci	sane_exit();
1882141cc406Sopenharmony_ci
1883141cc406Sopenharmony_ci the_exit:
1884141cc406Sopenharmony_ci	if (devname) free(devname);
1885141cc406Sopenharmony_ci	display_stats();
1886141cc406Sopenharmony_ci	return(0);
1887141cc406Sopenharmony_ci}
1888