sane-project-backends/frontend/scanimage.c

2213 wiersze
54 KiB
C
Czysty Zwykły widok Historia

1999-08-09 18:06:01 +00:00
/* scanimage -- command line scanning utility
Uses the SANE library.
Copyright (C) 1996, 1997, 1998 Andreas Beck and David Mosberger
Copyright (C) 1999 - 2005 by the SANE Project -- See AUTHORS and ChangeLog
for details.
1999-08-09 18:06:01 +00:00
For questions and comments contact the sane-devel mailinglist (see
http://www.sane-project.org/mailing-lists.html).
1999-08-09 18:06:01 +00:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef _AIX
# include "../include/lalloca.h" /* MUST come first for AIX! */
1999-08-09 18:06:01 +00:00
#endif
#include "../include/sane/config.h"
#include "../include/lalloca.h"
1999-08-09 18:06:01 +00:00
#include <assert.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
1999-08-09 18:06:01 +00:00
#include <sys/types.h>
#include <sys/stat.h>
1999-08-09 18:06:01 +00:00
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"
1999-08-09 18:06:01 +00:00
2000-08-12 15:11:46 +00:00
#include "stiff.h"
#include "../include/md5.h"
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
1999-08-09 18:06:01 +00:00
#ifndef HAVE_ATEXIT
# define atexit(func) on_exit(func, 0) /* works for SunOS, at least */
#endif
typedef struct
{
u_char *data;
int Bpp; /* bytes/pixel */
int width;
int height;
int x;
int y;
}
1999-08-09 18:06:01 +00:00
Image;
2000-08-12 15:11:46 +00:00
#define OPTION_FORMAT 1001
#define OPTION_MD5 1002
#define OPTION_BATCH_COUNT 1003
#define OPTION_BATCH_START_AT 1004
#define OPTION_BATCH_DOUBLE 1005
#define OPTION_BATCH_INCREMENT 1006
#define OPTION_BATCH_PROMPT 1007
#define BATCH_COUNT_UNLIMITED -1
2000-08-12 15:11:46 +00:00
static struct option basic_options[] = {
1999-08-09 18:06:01 +00:00
{"device-name", required_argument, NULL, 'd'},
{"list-devices", no_argument, NULL, 'L'},
{"formatted-device-list", required_argument, NULL, 'f'},
1999-08-09 18:06:01 +00:00
{"help", no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'},
{"progress", no_argument, NULL, 'p'},
1999-08-09 18:06:01 +00:00
{"test", no_argument, NULL, 'T'},
{"version", no_argument, NULL, 'V'},
{"buffer_size", no_argument, NULL, 'B'},
{"batch", optional_argument, NULL, 'b'},
{"batch-count", required_argument, NULL, OPTION_BATCH_COUNT},
{"batch-start", required_argument, NULL, OPTION_BATCH_START_AT},
{"batch-double", no_argument, NULL, OPTION_BATCH_DOUBLE},
{"batch-increment", required_argument, NULL, OPTION_BATCH_INCREMENT},
{"batch-prompt", no_argument, NULL, OPTION_BATCH_PROMPT},
2000-08-12 15:11:46 +00:00
{"format", required_argument, NULL, OPTION_FORMAT},
{"accept-md5-only", no_argument, NULL, OPTION_MD5},
{"icc-profile", required_argument, NULL, 'i'},
{"dont-scan", no_argument, NULL, 'n'},
{0, 0, NULL, 0}
1999-08-09 18:06:01 +00:00
};
2000-08-12 15:11:46 +00:00
#define OUTPUT_PNM 0
#define OUTPUT_TIFF 1
#define BASE_OPTSTRING "d:hi:Lf:nvBVTbp"
1999-08-09 18:06:01 +00:00
#define STRIP_HEIGHT 256 /* # lines we increment image height */
static struct option *all_options;
1999-08-09 18:06:01 +00:00
static int option_number_len;
static int *option_number;
1999-08-09 18:06:01 +00:00
static SANE_Handle device;
static int verbose;
static int progress = 0;
1999-08-09 18:06:01 +00:00
static int test;
2000-08-12 15:11:46 +00:00
static int output_format = OUTPUT_PNM;
1999-08-09 18:06:01 +00:00
static int help;
static int dont_scan = 0;
static const char *prog_name;
1999-08-09 18:06:01 +00:00
static SANE_Option_Descriptor window_option[2];
static int window[4];
2000-08-12 15:11:46 +00:00
static int resolution_optind = -1, resolution_value = 0;
1999-08-09 18:06:01 +00:00
static SANE_Word window_val[2];
static int window_val_user[2]; /* is width/height user-specified? */
static int accept_only_md5_auth = 0;
static const char *icc_profile = NULL;
2001-05-14 07:18:25 +00:00
static void fetch_options (SANE_Device * device);
static void scanimage_exit (void);
static SANE_Word tl_x = 0;
static SANE_Word tl_y = 0;
static SANE_Word br_x = 0;
static SANE_Word br_y = 0;
static SANE_Word w_x = 0;
static SANE_Word h_y = 0;
static SANE_Byte *buffer;
static size_t buffer_size;
static void
auth_callback (SANE_String_Const resource,
SANE_Char * username, SANE_Char * password)
{
char tmp[3 + 128 + SANE_MAX_USERNAME_LEN + SANE_MAX_PASSWORD_LEN], *wipe;
unsigned char md5digest[16];
int md5mode = 0, len, query_user = 1;
FILE *pass_file;
struct stat stat_buf;
*tmp = 0;
if (getenv ("HOME") != NULL)
{
if (strlen (getenv ("HOME")) < 500)
{
sprintf (tmp, "%s/.sane/pass", getenv ("HOME"));
}
}
if ((strlen (tmp) > 0) && (stat (tmp, &stat_buf) == 0))
{
if ((stat_buf.st_mode & 63) != 0)
{
fprintf (stderr, "%s has wrong permissions (use at least 0600)\n",
tmp);
}
else
{
if ((pass_file = fopen (tmp, "r")) != NULL)
{
if (strstr (resource, "$MD5$") != NULL)
len = (strstr (resource, "$MD5$") - resource);
else
len = strlen (resource);
while (fgets (tmp, sizeof(tmp), pass_file))
{
if ((strlen (tmp) > 0) && (tmp[strlen (tmp) - 1] == '\n'))
tmp[strlen (tmp) - 1] = 0;
if ((strlen (tmp) > 0) && (tmp[strlen (tmp) - 1] == '\r'))
tmp[strlen (tmp) - 1] = 0;
if (strchr (tmp, ':') != NULL)
{
if (strchr (strchr (tmp, ':') + 1, ':') != NULL)
{
if ((strncmp
(strchr (strchr (tmp, ':') + 1, ':') + 1,
resource, len) == 0)
&&
((int) strlen
(strchr (strchr (tmp, ':') + 1, ':') + 1) ==
len))
{
if ((strchr (tmp, ':') - tmp) <
SANE_MAX_USERNAME_LEN)
{
if ((strchr (strchr (tmp, ':') + 1, ':') -
(strchr (tmp, ':') + 1)) <
SANE_MAX_PASSWORD_LEN)
{
strncpy (username, tmp,
strchr (tmp, ':') - tmp);
username[strchr (tmp, ':') - tmp] = 0;
strncpy (password,
strchr (tmp, ':') + 1,
strchr (strchr (tmp, ':') + 1,
':') -
(strchr (tmp, ':') + 1));
password[strchr
(strchr (tmp, ':') + 1,
':') - (strchr (tmp,
':') + 1)] =
0;
query_user = 0;
break;
}
}
}
}
}
}
fclose (pass_file);
}
}
}
1999-08-09 18:06:01 +00:00
if (strstr (resource, "$MD5$") != NULL)
{
md5mode = 1;
len = (strstr (resource, "$MD5$") - resource);
if (query_user == 1)
fprintf (stderr, "Authentification required for resource %*.*s. "
"Enter username: ", len, len, resource);
}
else
{
if (accept_only_md5_auth != 0)
{
fprintf (stderr, "ERROR: backend requested plain-text password\n");
return;
}
else
{
fprintf (stderr,
"WARNING: backend requested plain-text password\n");
query_user = 1;
}
if (query_user == 1)
fprintf (stderr,
"Authentification required for resource %s. Enter username: ",
resource);
}
if (query_user == 1)
fgets (username, SANE_MAX_USERNAME_LEN, stdin);
if ((strlen (username)) && (username[strlen (username) - 1] == '\n'))
username[strlen (username) - 1] = 0;
if (query_user == 1)
{
strcpy (password, (wipe = getpass ("Enter password: ")));
memset (wipe, 0, strlen (password));
}
if (md5mode)
{
sprintf (tmp, "%.128s%.*s", (strstr (resource, "$MD5$")) + 5,
SANE_MAX_PASSWORD_LEN - 1, password);
md5_buffer (tmp, strlen (tmp), md5digest);
memset (password, 0, SANE_MAX_PASSWORD_LEN);
sprintf (password, "$MD5$%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x",
md5digest[0], md5digest[1],
md5digest[2], md5digest[3],
md5digest[4], md5digest[5],
md5digest[6], md5digest[7],
md5digest[8], md5digest[9],
md5digest[10], md5digest[11],
md5digest[12], md5digest[13], md5digest[14], md5digest[15]);
}
}
1999-08-09 18:06:01 +00:00
static RETSIGTYPE
sighandler (int signum)
{
static SANE_Bool first_time = SANE_TRUE;
1999-08-09 18:06:01 +00:00
if (device)
{
fprintf (stderr, "%s: received signal %d\n", prog_name, signum);
if (first_time)
{
first_time = SANE_FALSE;
fprintf (stderr, "%s: trying to stop scanner\n", prog_name);
sane_cancel (device);
}
else
{
fprintf (stderr, "%s: aborting\n", prog_name);
_exit (0);
}
1999-08-09 18:06:01 +00:00
}
}
static void
print_unit (SANE_Unit unit)
{
switch (unit)
{
case SANE_UNIT_NONE:
break;
case SANE_UNIT_PIXEL:
fputs ("pel", stdout);
break;
case SANE_UNIT_BIT:
fputs ("bit", stdout);
break;
case SANE_UNIT_MM:
fputs ("mm", stdout);
break;
case SANE_UNIT_DPI:
fputs ("dpi", stdout);
break;
case SANE_UNIT_PERCENT:
fputc ('%', stdout);
break;
case SANE_UNIT_MICROSECOND:
fputs ("us", stdout);
break;
1999-08-09 18:06:01 +00:00
}
}
static void
print_option (SANE_Device * device, int opt_num, char short_name)
1999-08-09 18:06:01 +00:00
{
const char *str, *last_break, *start;
const SANE_Option_Descriptor *opt;
SANE_Bool not_first = SANE_FALSE;
int i, column;
SANE_Word maxwindow = 0;
1999-08-09 18:06:01 +00:00
opt = sane_get_option_descriptor (device, opt_num);
if (short_name)
printf (" -%c", short_name);
else
printf (" --%s", opt->name);
if (opt->type == SANE_TYPE_BOOL)
{
fputs ("[=(", stdout);
if (opt->cap & SANE_CAP_AUTOMATIC)
fputs ("auto|", stdout);
fputs ("yes|no)]", stdout);
}
else if (opt->type != SANE_TYPE_BUTTON)
{
fputc (' ', stdout);
if (opt->cap & SANE_CAP_AUTOMATIC)
{
fputs ("auto|", stdout);
not_first = SANE_TRUE;
}
switch (opt->constraint_type)
{
case SANE_CONSTRAINT_NONE:
switch (opt->type)
{
case SANE_TYPE_INT:
fputs ("<int>", stdout);
break;
case SANE_TYPE_FIXED:
fputs ("<float>", stdout);
break;
case SANE_TYPE_STRING:
fputs ("<string>", stdout);
break;
1999-08-09 18:06:01 +00:00
default:
break;
}
if (opt->type != SANE_TYPE_STRING && opt->size
2000-09-24 Henning Meier-Geinitz <hmg@gmx.de> * backend/mustek.*: Update to Mustek backend 1.0-97. This is a development version and is only tested for three-pass scanners. Added support for Paragon 1200 SP Pro and ScanExpress A3 SP. Removed detection of " C04" and " C12" (don't seem to exist). Changed SCSI request scheme. Sane_read can read more than 4096 bytes from pipe. Minimum dpi is 30 now. Fixed LD correction for Paragon 1200SP 1.06 and 1.11. Read SCSI buffer into big block and do LD after that for Paragon one-pass scanners. New option "blocksize" in mustek.conf. Better detection of the scanner type (three-pass, Paragon I, ...). Fixed possible segmentation faults in Paragon 600 II N LD code, scsi_sense_wait_ready and sense_handler. Decreased maximum scan size of Paragon 800 II SP. For three-pass scanners: fixed stop_scan and speed code, scan area is in pixels now, added RGB brightness and contrast, use +-100% for brightness and contrast. Return SANE_STATUS_CANCELLED when scan was cancelled. Option "force backtracking" is enabled by default, "scan speed" is set to "fastest". Added option "force-warn" for mustek.conf. Rewrote halftone mode. Better error handling and more debug output. * TODO: Clarified point about image data polarity. Moved point about xscanimage not updating the progress bar for 3pass scanners to frontend section. Removed umax entry in "frontends". Removed entry about alpha channel (was added to LEVEL2 file). * LEVEL2: Add point about image data polarity. * frontend/scanimage.c: Removed some warnings.
2000-09-24 19:38:35 +00:00
> (SANE_Int) sizeof (SANE_Word))
1999-08-09 18:06:01 +00:00
fputs (",...", stdout);
break;
case SANE_CONSTRAINT_RANGE:
if (opt->type == SANE_TYPE_INT)
{
if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
{
maxwindow = (opt->constraint.range->max) - tl_x;
printf ("%d..%d", opt->constraint.range->min, maxwindow);
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
{
maxwindow = (opt->constraint.range->max) - tl_y;
printf ("%d..%d", opt->constraint.range->min, maxwindow);
}
else
{
printf ("%d..%d",
opt->constraint.range->min,
opt->constraint.range->max);
}
1999-08-09 18:06:01 +00:00
print_unit (opt->unit);
2000-09-24 Henning Meier-Geinitz <hmg@gmx.de> * backend/mustek.*: Update to Mustek backend 1.0-97. This is a development version and is only tested for three-pass scanners. Added support for Paragon 1200 SP Pro and ScanExpress A3 SP. Removed detection of " C04" and " C12" (don't seem to exist). Changed SCSI request scheme. Sane_read can read more than 4096 bytes from pipe. Minimum dpi is 30 now. Fixed LD correction for Paragon 1200SP 1.06 and 1.11. Read SCSI buffer into big block and do LD after that for Paragon one-pass scanners. New option "blocksize" in mustek.conf. Better detection of the scanner type (three-pass, Paragon I, ...). Fixed possible segmentation faults in Paragon 600 II N LD code, scsi_sense_wait_ready and sense_handler. Decreased maximum scan size of Paragon 800 II SP. For three-pass scanners: fixed stop_scan and speed code, scan area is in pixels now, added RGB brightness and contrast, use +-100% for brightness and contrast. Return SANE_STATUS_CANCELLED when scan was cancelled. Option "force backtracking" is enabled by default, "scan speed" is set to "fastest". Added option "force-warn" for mustek.conf. Rewrote halftone mode. Better error handling and more debug output. * TODO: Clarified point about image data polarity. Moved point about xscanimage not updating the progress bar for 3pass scanners to frontend section. Removed umax entry in "frontends". Removed entry about alpha channel (was added to LEVEL2 file). * LEVEL2: Add point about image data polarity. * frontend/scanimage.c: Removed some warnings.
2000-09-24 19:38:35 +00:00
if (opt->size > (SANE_Int) sizeof (SANE_Word))
1999-08-09 18:06:01 +00:00
fputs (",...", stdout);
if (opt->constraint.range->quant)
printf (" (in steps of %d)", opt->constraint.range->quant);
1999-08-09 18:06:01 +00:00
}
else
{
if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
{
maxwindow = (opt->constraint.range->max) - tl_x;
printf ("%g..%g",
SANE_UNFIX (opt->constraint.range->min),
SANE_UNFIX (maxwindow));
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
{
maxwindow = (opt->constraint.range->max) - tl_y;
printf ("%g..%g",
SANE_UNFIX (opt->constraint.range->min),
SANE_UNFIX (maxwindow));
}
else
{
printf ("%g..%g",
SANE_UNFIX (opt->constraint.range->min),
SANE_UNFIX (opt->constraint.range->max));
}
1999-08-09 18:06:01 +00:00
print_unit (opt->unit);
2000-09-24 Henning Meier-Geinitz <hmg@gmx.de> * backend/mustek.*: Update to Mustek backend 1.0-97. This is a development version and is only tested for three-pass scanners. Added support for Paragon 1200 SP Pro and ScanExpress A3 SP. Removed detection of " C04" and " C12" (don't seem to exist). Changed SCSI request scheme. Sane_read can read more than 4096 bytes from pipe. Minimum dpi is 30 now. Fixed LD correction for Paragon 1200SP 1.06 and 1.11. Read SCSI buffer into big block and do LD after that for Paragon one-pass scanners. New option "blocksize" in mustek.conf. Better detection of the scanner type (three-pass, Paragon I, ...). Fixed possible segmentation faults in Paragon 600 II N LD code, scsi_sense_wait_ready and sense_handler. Decreased maximum scan size of Paragon 800 II SP. For three-pass scanners: fixed stop_scan and speed code, scan area is in pixels now, added RGB brightness and contrast, use +-100% for brightness and contrast. Return SANE_STATUS_CANCELLED when scan was cancelled. Option "force backtracking" is enabled by default, "scan speed" is set to "fastest". Added option "force-warn" for mustek.conf. Rewrote halftone mode. Better error handling and more debug output. * TODO: Clarified point about image data polarity. Moved point about xscanimage not updating the progress bar for 3pass scanners to frontend section. Removed umax entry in "frontends". Removed entry about alpha channel (was added to LEVEL2 file). * LEVEL2: Add point about image data polarity. * frontend/scanimage.c: Removed some warnings.
2000-09-24 19:38:35 +00:00
if (opt->size > (SANE_Int) sizeof (SANE_Word))
1999-08-09 18:06:01 +00:00
fputs (",...", stdout);
if (opt->constraint.range->quant)
printf (" (in steps of %g)",
SANE_UNFIX (opt->constraint.range->quant));
1999-08-09 18:06:01 +00:00
}
break;
case SANE_CONSTRAINT_WORD_LIST:
for (i = 0; i < opt->constraint.word_list[0]; ++i)
{
if (not_first)
fputc ('|', stdout);
not_first = SANE_TRUE;
if (opt->type == SANE_TYPE_INT)
printf ("%d", opt->constraint.word_list[i + 1]);
else
printf ("%g", SANE_UNFIX (opt->constraint.word_list[i + 1]));
1999-08-09 18:06:01 +00:00
}
print_unit (opt->unit);
2000-09-24 Henning Meier-Geinitz <hmg@gmx.de> * backend/mustek.*: Update to Mustek backend 1.0-97. This is a development version and is only tested for three-pass scanners. Added support for Paragon 1200 SP Pro and ScanExpress A3 SP. Removed detection of " C04" and " C12" (don't seem to exist). Changed SCSI request scheme. Sane_read can read more than 4096 bytes from pipe. Minimum dpi is 30 now. Fixed LD correction for Paragon 1200SP 1.06 and 1.11. Read SCSI buffer into big block and do LD after that for Paragon one-pass scanners. New option "blocksize" in mustek.conf. Better detection of the scanner type (three-pass, Paragon I, ...). Fixed possible segmentation faults in Paragon 600 II N LD code, scsi_sense_wait_ready and sense_handler. Decreased maximum scan size of Paragon 800 II SP. For three-pass scanners: fixed stop_scan and speed code, scan area is in pixels now, added RGB brightness and contrast, use +-100% for brightness and contrast. Return SANE_STATUS_CANCELLED when scan was cancelled. Option "force backtracking" is enabled by default, "scan speed" is set to "fastest". Added option "force-warn" for mustek.conf. Rewrote halftone mode. Better error handling and more debug output. * TODO: Clarified point about image data polarity. Moved point about xscanimage not updating the progress bar for 3pass scanners to frontend section. Removed umax entry in "frontends". Removed entry about alpha channel (was added to LEVEL2 file). * LEVEL2: Add point about image data polarity. * frontend/scanimage.c: Removed some warnings.
2000-09-24 19:38:35 +00:00
if (opt->size > (SANE_Int) sizeof (SANE_Word))
1999-08-09 18:06:01 +00:00
fputs (",...", stdout);
break;
case SANE_CONSTRAINT_STRING_LIST:
for (i = 0; opt->constraint.string_list[i]; ++i)
{
if (i > 0)
fputc ('|', stdout);
fputs (opt->constraint.string_list[i], stdout);
}
break;
}
}
if (opt->type == SANE_TYPE_STRING || opt->size == sizeof (SANE_Word))
{
/* print current option value */
if (SANE_OPTION_IS_ACTIVE (opt->cap))
1999-08-09 18:06:01 +00:00
{
void *val = alloca (opt->size);
sane_control_option (device, opt_num, SANE_ACTION_GET_VALUE, val,
0);
fputs (" [", stdout);
1999-08-09 18:06:01 +00:00
switch (opt->type)
{
case SANE_TYPE_BOOL:
fputs (*(SANE_Bool *) val ? "yes" : "no", stdout);
1999-08-09 18:06:01 +00:00
break;
case SANE_TYPE_INT:
if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
{
tl_x = (*(SANE_Fixed *) val);
printf ("%d", tl_x);
}
else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
{
tl_y = (*(SANE_Fixed *) val);
printf ("%d", tl_y);
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
{
br_x = (*(SANE_Fixed *) val);
w_x = br_x - tl_x;
printf ("%d", w_x);
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
{
br_y = (*(SANE_Fixed *) val);
h_y = br_y - tl_y;
printf ("%d", h_y);
}
else
printf ("%d", *(SANE_Int *) val);
1999-08-09 18:06:01 +00:00
break;
case SANE_TYPE_FIXED:
if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
{
tl_x = (*(SANE_Fixed *) val);
printf ("%g", SANE_UNFIX (tl_x));
}
else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
{
tl_y = (*(SANE_Fixed *) val);
printf ("%g", SANE_UNFIX (tl_y));
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
{
br_x = (*(SANE_Fixed *) val);
w_x = br_x - tl_x;
printf ("%g", SANE_UNFIX (w_x));
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
{
br_y = (*(SANE_Fixed *) val);
h_y = br_y - tl_y;
printf ("%g", SANE_UNFIX (h_y));
}
else
printf ("%g", SANE_UNFIX (*(SANE_Fixed *) val));
1999-08-09 18:06:01 +00:00
break;
case SANE_TYPE_STRING:
fputs ((char *) val, stdout);
break;
default:
break;
}
fputc (']', stdout);
1999-08-09 18:06:01 +00:00
}
}
if (!SANE_OPTION_IS_ACTIVE (opt->cap))
fputs (" [inactive]", stdout);
1999-08-09 18:06:01 +00:00
fputs ("\n ", stdout);
switch (short_name)
{
case 'x':
fputs ("Width of scan-area.", stdout);
break;
case 'y':
fputs ("Height of scan-area.", stdout);
break;
default:
column = 8;
last_break = 0;
start = opt->desc;
1999-08-09 18:06:01 +00:00
for (str = opt->desc; *str; ++str)
{
++column;
if (*str == ' ')
last_break = str;
if (column >= 79 && last_break)
{
while (start < last_break)
fputc (*start++, stdout);
start = last_break + 1; /* skip blank */
fputs ("\n ", stdout);
column = 8 + (str - start);
}
}
while (*start)
fputc (*start++, stdout);
}
fputc ('\n', stdout);
}
/* A scalar has the following syntax:
V [ U ]
V is the value of the scalar. It is either an integer or a
floating point number, depending on the option type.
U is an optional unit. If not specified, the default unit is used.
The following table lists which units are supported depending on
what the option's default unit is:
Option's unit: Allowed units:
SANE_UNIT_NONE:
SANE_UNIT_PIXEL: pel
SANE_UNIT_BIT: b (bit), B (byte)
SANE_UNIT_MM: mm (millimeter), cm (centimeter), in or " (inches),
SANE_UNIT_DPI: dpi
SANE_UNIT_PERCENT: %
SANE_UNIT_PERCENT: us
*/
static const char *
parse_scalar (const SANE_Option_Descriptor * opt, const char *str,
1999-08-09 18:06:01 +00:00
SANE_Word * value)
{
char *end;
1999-08-09 18:06:01 +00:00
double v;
if (opt->type == SANE_TYPE_FIXED)
v = strtod (str, &end) * (1 << SANE_FIXED_SCALE_SHIFT);
else
v = strtol (str, &end, 10);
if (str == end)
{
fprintf (stderr,
"%s: option --%s: bad option value (rest of option: %s)\n",
prog_name, opt->name, str);
exit (1);
}
str = end;
switch (opt->unit)
{
case SANE_UNIT_NONE:
case SANE_UNIT_PIXEL:
break;
case SANE_UNIT_BIT:
if (*str == 'b' || *str == 'B')
{
if (*str++ == 'B')
v *= 8;
}
break;
case SANE_UNIT_MM:
if (str[0] == '\0')
v *= 1.0; /* default to mm */
1999-08-09 18:06:01 +00:00
else if (strcmp (str, "mm") == 0)
str += sizeof ("mm") - 1;
else if (strcmp (str, "cm") == 0)
{
str += sizeof ("cm") - 1;
v *= 10.0;
}
else if (strcmp (str, "in") == 0 || *str == '"')
{
if (*str++ != '"')
++str;
v *= 25.4; /* 25.4 mm/inch */
1999-08-09 18:06:01 +00:00
}
else
{
fprintf (stderr,
"%s: option --%s: illegal unit (rest of option: %s)\n",
prog_name, opt->name, str);
return 0;
}
break;
case SANE_UNIT_DPI:
if (strcmp (str, "dpi") == 0)
str += sizeof ("dpi") - 1;
break;
case SANE_UNIT_PERCENT:
if (*str == '%')
++str;
break;
case SANE_UNIT_MICROSECOND:
if (strcmp (str, "us") == 0)
str += sizeof ("us") - 1;
break;
}
if(v < 0){
*value = v - 0.5;
}
else{
*value = v + 0.5;
}
1999-08-09 18:06:01 +00:00
return str;
}
/* A vector has the following syntax:
[ '[' I ']' ] S { [','|'-'] [ '[' I ']' S }
The number in brackets (I), if present, determines the index of the
vector element to be set next. If I is not present, the value of
last index used plus 1 is used. The first index value used is 0
unless I is present.
S is a scalar value as defined by parse_scalar().
If two consecutive value specs are separated by a comma (,) their
values are set independently. If they are separated by a dash (-),
they define the endpoints of a line and all vector values between
the two endpoints are set according to the value of the
interpolated line. For example, [0]15-[255]15 defines a vector of
256 elements whose value is 15. Similarly, [0]0-[255]255 defines a
vector of 256 elements whose value starts at 0 and increases to
255. */
static void
parse_vector (const SANE_Option_Descriptor * opt, const char *str,
1999-08-09 18:06:01 +00:00
SANE_Word * vector, size_t vector_length)
{
SANE_Word value, prev_value = 0;
int index = -1, prev_index = 0;
char *end, separator = '\0';
1999-08-09 18:06:01 +00:00
/* initialize vector to all zeroes: */
memset (vector, 0, vector_length * sizeof (SANE_Word));
do
{
if (*str == '[')
{
/* read index */
index = strtol (++str, &end, 10);
if (str == end || *end != ']')
{
fprintf (stderr, "%s: option --%s: closing bracket missing "
"(rest of option: %s)\n", prog_name, opt->name, str);
exit (1);
}
str = end + 1;
}
else
++index;
1999-08-09 18:06:01 +00:00
if (index < 0 || index >= (int) vector_length)
{
fprintf (stderr,
"%s: option --%s: index %d out of range [0..%ld]\n",
prog_name, opt->name, index, (long) vector_length - 1);
exit (1);
}
1999-08-09 18:06:01 +00:00
/* read value */
str = parse_scalar (opt, str, &value);
if (!str)
1999-08-09 18:06:01 +00:00
exit (1);
if (*str && *str != '-' && *str != ',')
{
fprintf (stderr,
"%s: option --%s: illegal separator (rest of option: %s)\n",
prog_name, opt->name, str);
exit (1);
}
1999-08-09 18:06:01 +00:00
/* store value: */
vector[index] = value;
if (separator == '-')
{
/* interpolate */
double v, slope;
int i;
1999-08-09 18:06:01 +00:00
v = (double) prev_value;
slope = ((double) value - v) / (index - prev_index);
1999-08-09 18:06:01 +00:00
for (i = prev_index + 1; i < index; ++i)
{
v += slope;
vector[i] = (SANE_Word) v;
}
}
prev_index = index;
prev_value = value;
separator = *str++;
}
while (separator == ',' || separator == '-');
1999-08-09 18:06:01 +00:00
if (verbose > 2)
{
int i;
fprintf (stderr, "%s: value for --%s is: ", prog_name, opt->name);
2000-09-24 Henning Meier-Geinitz <hmg@gmx.de> * backend/mustek.*: Update to Mustek backend 1.0-97. This is a development version and is only tested for three-pass scanners. Added support for Paragon 1200 SP Pro and ScanExpress A3 SP. Removed detection of " C04" and " C12" (don't seem to exist). Changed SCSI request scheme. Sane_read can read more than 4096 bytes from pipe. Minimum dpi is 30 now. Fixed LD correction for Paragon 1200SP 1.06 and 1.11. Read SCSI buffer into big block and do LD after that for Paragon one-pass scanners. New option "blocksize" in mustek.conf. Better detection of the scanner type (three-pass, Paragon I, ...). Fixed possible segmentation faults in Paragon 600 II N LD code, scsi_sense_wait_ready and sense_handler. Decreased maximum scan size of Paragon 800 II SP. For three-pass scanners: fixed stop_scan and speed code, scan area is in pixels now, added RGB brightness and contrast, use +-100% for brightness and contrast. Return SANE_STATUS_CANCELLED when scan was cancelled. Option "force backtracking" is enabled by default, "scan speed" is set to "fastest". Added option "force-warn" for mustek.conf. Rewrote halftone mode. Better error handling and more debug output. * TODO: Clarified point about image data polarity. Moved point about xscanimage not updating the progress bar for 3pass scanners to frontend section. Removed umax entry in "frontends". Removed entry about alpha channel (was added to LEVEL2 file). * LEVEL2: Add point about image data polarity. * frontend/scanimage.c: Removed some warnings.
2000-09-24 19:38:35 +00:00
for (i = 0; i < (int) vector_length; ++i)
1999-08-09 18:06:01 +00:00
if (opt->type == SANE_TYPE_FIXED)
fprintf (stderr, "%g ", SANE_UNFIX (vector[i]));
1999-08-09 18:06:01 +00:00
else
fprintf (stderr, "%d ", vector[i]);
fputc ('\n', stderr);
}
}
2001-05-14 07:18:25 +00:00
static void
1999-08-09 18:06:01 +00:00
fetch_options (SANE_Device * device)
{
const SANE_Option_Descriptor *opt;
1999-08-09 18:06:01 +00:00
SANE_Int num_dev_options;
int i, option_count;
/* and now build the full table of long options: */
sane_control_option (device, 0, SANE_ACTION_GET_VALUE, &num_dev_options, 0);
option_count = 0;
for (i = 0; i < num_dev_options; ++i)
{
opt = sane_get_option_descriptor (device, i);
if (!SANE_OPTION_IS_SETTABLE (opt->cap))
continue;
option_number[option_count] = i;
all_options[option_count].name = (char *) opt->name;
all_options[option_count].flag = 0;
all_options[option_count].val = 0;
if (opt->type == SANE_TYPE_BOOL)
all_options[option_count].has_arg = optional_argument;
else if (opt->type == SANE_TYPE_BUTTON)
all_options[option_count].has_arg = no_argument;
else
all_options[option_count].has_arg = required_argument;
2000-08-12 15:11:46 +00:00
/* Look for scan resolution */
if ((opt->type == SANE_TYPE_FIXED || opt->type == SANE_TYPE_INT)
&& opt->size == sizeof (SANE_Int)
&& (opt->unit == SANE_UNIT_DPI)
&& (strcmp (opt->name, SANE_NAME_SCAN_RESOLUTION) == 0))
resolution_optind = i;
2000-08-12 15:11:46 +00:00
1999-08-09 18:06:01 +00:00
/* Keep track of top-left corner options (if they exist at
all) and replace the bottom-right corner options by a
width/height option (if they exist at all). */
1999-08-09 18:06:01 +00:00
if ((opt->type == SANE_TYPE_FIXED || opt->type == SANE_TYPE_INT)
&& opt->size == sizeof (SANE_Int)
&& (opt->unit == SANE_UNIT_MM || opt->unit == SANE_UNIT_PIXEL))
{
if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
{
window[2] = i;
all_options[option_count].val = 'l';
}
else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
{
window[3] = i;
all_options[option_count].val = 't';
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
{
window[0] = i;
all_options[option_count].name = "width";
all_options[option_count].val = 'x';
1999-08-09 18:06:01 +00:00
window_option[0] = *opt;
window_option[0].title = "Scan width";
window_option[0].desc = "Width of scanning area.";
if (!window_val_user[0])
sane_control_option (device, i, SANE_ACTION_GET_VALUE,
&window_val[0], 0);
}
else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
{
window[1] = i;
all_options[option_count].name = "height";
all_options[option_count].val = 'y';
1999-08-09 18:06:01 +00:00
window_option[1] = *opt;
window_option[1].title = "Scan height";
window_option[1].desc = "Height of scanning area.";
if (!window_val_user[1])
sane_control_option (device, i, SANE_ACTION_GET_VALUE,
&window_val[1], 0);
}
}
++option_count;
}
memcpy (all_options + option_count, basic_options, sizeof (basic_options));
option_count += NELEMS (basic_options);
1999-08-09 18:06:01 +00:00
memset (all_options + option_count, 0, sizeof (all_options[0]));
/* Initialize width & height options based on backend default
values for top-left x/y and bottom-right x/y: */
for (i = 0; i < 2; ++i)
{
if (window[i] && window[i + 2] && !window_val_user[i])
{
SANE_Word pos;
sane_control_option (device, window[i + 2],
SANE_ACTION_GET_VALUE, &pos, 0);
window_val[i] = window_val[i] - pos + 1;
}
}
}
static void
set_option (SANE_Handle device, int optnum, void *valuep)
1999-08-09 18:06:01 +00:00
{
const SANE_Option_Descriptor *opt;
1999-08-09 18:06:01 +00:00
SANE_Status status;
SANE_Word orig = 0;
SANE_Int info;
opt = sane_get_option_descriptor (device, optnum);
if (opt && (!SANE_OPTION_IS_ACTIVE (opt->cap)))
{
if (verbose > 0)
fprintf (stderr, "%s: ignored request to set inactive option %s\n",
prog_name, opt->name);
return;
}
1999-08-09 18:06:01 +00:00
if (opt->size == sizeof (SANE_Word) && opt->type != SANE_TYPE_STRING)
orig = *(SANE_Word *) valuep;
status = sane_control_option (device, optnum, SANE_ACTION_SET_VALUE,
valuep, &info);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: setting of option --%s failed (%s)\n",
prog_name, opt->name, sane_strstatus (status));
exit (1);
}
if ((info & SANE_INFO_INEXACT) && opt->size == sizeof (SANE_Word))
{
if (opt->type == SANE_TYPE_INT)
fprintf (stderr, "%s: rounded value of %s from %d to %d\n",
prog_name, opt->name, orig, *(SANE_Word *) valuep);
else if (opt->type == SANE_TYPE_FIXED)
fprintf (stderr, "%s: rounded value of %s from %g to %g\n",
prog_name, opt->name,
SANE_UNFIX (orig), SANE_UNFIX (*(SANE_Word *) valuep));
1999-08-09 18:06:01 +00:00
}
if (info & SANE_INFO_RELOAD_OPTIONS)
fetch_options (device);
}
static void
process_backend_option (SANE_Handle device, int optnum, const char *optarg)
1999-08-09 18:06:01 +00:00
{
static SANE_Word *vector = 0;
1999-08-09 18:06:01 +00:00
static size_t vector_size = 0;
const SANE_Option_Descriptor *opt;
1999-08-09 18:06:01 +00:00
size_t vector_length;
SANE_Status status;
SANE_Word value;
void *valuep;
1999-08-09 18:06:01 +00:00
opt = sane_get_option_descriptor (device, optnum);
if (!SANE_OPTION_IS_ACTIVE (opt->cap))
1999-08-09 18:06:01 +00:00
{
fprintf (stderr, "%s: attempted to set inactive option %s\n",
prog_name, opt->name);
exit (1);
}
if ((opt->cap & SANE_CAP_AUTOMATIC) && optarg &&
strncasecmp (optarg, "auto", 4) == 0)
1999-08-09 18:06:01 +00:00
{
status = sane_control_option (device, optnum, SANE_ACTION_SET_AUTO,
0, 0);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr,
"%s: failed to set option --%s to automatic (%s)\n",
1999-08-09 18:06:01 +00:00
prog_name, opt->name, sane_strstatus (status));
exit (1);
}
return;
}
valuep = &value;
switch (opt->type)
{
case SANE_TYPE_BOOL:
value = 1; /* no argument means option is set */
1999-08-09 18:06:01 +00:00
if (optarg)
{
if (strncasecmp (optarg, "yes", strlen (optarg)) == 0)
value = 1;
else if (strncasecmp (optarg, "no", strlen (optarg)) == 0)
value = 0;
else
{
fprintf (stderr, "%s: option --%s: bad option value `%s'\n",
prog_name, opt->name, optarg);
exit (1);
}
}
break;
case SANE_TYPE_INT:
case SANE_TYPE_FIXED:
/* ensure vector is long enough: */
vector_length = opt->size / sizeof (SANE_Word);
if (vector_size < vector_length)
{
vector_size = vector_length;
vector = realloc (vector, vector_length * sizeof (SANE_Word));
if (!vector)
{
fprintf (stderr, "%s: out of memory\n", prog_name);
exit (1);
}
}
parse_vector (opt, optarg, vector, vector_length);
valuep = vector;
break;
case SANE_TYPE_STRING:
valuep = malloc (opt->size);
if (!valuep)
{
fprintf (stderr, "%s: out of memory\n", prog_name);
exit (1);
}
strncpy (valuep, optarg, opt->size);
((char *) valuep)[opt->size - 1] = 0;
1999-08-09 18:06:01 +00:00
break;
case SANE_TYPE_BUTTON:
value = 0; /* value doesn't matter */
1999-08-09 18:06:01 +00:00
break;
default:
fprintf (stderr, "%s: duh, got unknown option type %d\n",
prog_name, opt->type);
return;
}
set_option (device, optnum, valuep);
}
static void
write_pnm_header (SANE_Frame format, int width, int height, int depth)
{
2000-08-12 15:11:46 +00:00
/* The netpbm-package does not define raw image data with maxval > 255. */
/* But writing maxval 65535 for 16bit data gives at least a chance */
/* to read the image. */
1999-08-09 18:06:01 +00:00
switch (format)
{
case SANE_FRAME_RED:
case SANE_FRAME_GREEN:
case SANE_FRAME_BLUE:
case SANE_FRAME_RGB:
2000-08-12 15:11:46 +00:00
printf ("P6\n# SANE data follows\n%d %d\n%d\n", width, height,
(depth <= 8) ? 255 : 65535);
1999-08-09 18:06:01 +00:00
break;
default:
if (depth == 1)
printf ("P4\n# SANE data follows\n%d %d\n", width, height);
else
2000-08-12 15:11:46 +00:00
printf ("P5\n# SANE data follows\n%d %d\n%d\n", width, height,
(depth <= 8) ? 255 : 65535);
1999-08-09 18:06:01 +00:00
break;
}
#ifdef __EMX__ /* OS2 - write in binary mode. */
_fsetmode (stdout, "b");
1999-08-09 18:06:01 +00:00
#endif
}
static void *
advance (Image * image)
1999-08-09 18:06:01 +00:00
{
if (++image->x >= image->width)
{
image->x = 0;
if (++image->y >= image->height || !image->data)
{
size_t old_size = 0, new_size;
if (image->data)
old_size = image->height * image->width * image->Bpp;
image->height += STRIP_HEIGHT;
new_size = image->height * image->width * image->Bpp;
if (image->data)
image->data = realloc (image->data, new_size);
else
image->data = malloc (new_size);
if (image->data)
memset (image->data + old_size, 0, new_size - old_size);
}
}
if (!image->data)
fprintf (stderr, "%s: can't allocate image buffer (%dx%d)\n",
prog_name, image->width, image->height);
1999-08-09 18:06:01 +00:00
return image->data;
}
static SANE_Status
1999-08-09 18:06:01 +00:00
scan_it (void)
{
int i, len, first_frame = 1, offset = 0, must_buffer = 0, hundred_percent;
SANE_Byte min = 0xff, max = 0;
1999-08-09 18:06:01 +00:00
SANE_Parameters parm;
SANE_Status status;
Image image = { 0, 0, 0, 0, 0, 0 };
static const char *format_name[] = {
"gray", "RGB", "red", "green", "blue", "JPEG"
};
SANE_Word total_bytes = 0, expected_bytes;
SANE_Int hang_over = -1;
1999-08-09 18:06:01 +00:00
buffer = malloc (buffer_size);
1999-08-09 18:06:01 +00:00
do
{
if (!first_frame)
1999-08-09 18:06:01 +00:00
{
status = sane_start (device);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_start: %s\n",
prog_name, sane_strstatus (status));
goto cleanup;
}
1999-08-09 18:06:01 +00:00
}
status = sane_get_parameters (device, &parm);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_get_parameters: %s\n",
prog_name, sane_strstatus (status));
goto cleanup;
}
if (verbose)
{
if (first_frame)
{
if (parm.lines >= 0)
fprintf (stderr, "%s: scanning image of size %dx%d pixels at "
"%d bits/pixel\n",
prog_name, parm.pixels_per_line, parm.lines,
8 * parm.bytes_per_line / parm.pixels_per_line);
else
fprintf (stderr, "%s: scanning image %d pixels wide and "
"variable height at %d bits/pixel\n",
prog_name, parm.pixels_per_line,
8 * parm.bytes_per_line / parm.pixels_per_line);
}
1999-08-09 18:06:01 +00:00
fprintf (stderr, "%s: acquiring %s frame\n", prog_name,
parm.format <= SANE_FRAME_JPEG ? format_name[parm.format]:"Unknown");
1999-08-09 18:06:01 +00:00
}
if (first_frame)
{
switch (parm.format)
{
case SANE_FRAME_RED:
case SANE_FRAME_GREEN:
case SANE_FRAME_BLUE:
assert (parm.depth == 8);
1999-08-09 18:06:01 +00:00
must_buffer = 1;
offset = parm.format - SANE_FRAME_RED;
break;
case SANE_FRAME_RGB:
2000-08-12 15:11:46 +00:00
assert ((parm.depth == 8) || (parm.depth == 16));
1999-08-09 18:06:01 +00:00
case SANE_FRAME_GRAY:
assert ((parm.depth == 1) || (parm.depth == 8)
|| (parm.depth == 16));
1999-08-09 18:06:01 +00:00
if (parm.lines < 0)
{
must_buffer = 1;
offset = 0;
}
else
{
if (output_format == OUTPUT_TIFF)
sanei_write_tiff_header (parm.format,
parm.pixels_per_line, parm.lines,
parm.depth, resolution_value,
icc_profile);
else
write_pnm_header (parm.format, parm.pixels_per_line,
parm.lines, parm.depth);
}
1999-08-09 18:06:01 +00:00
break;
default:
break;
1999-08-09 18:06:01 +00:00
}
if (must_buffer)
{
/* We're either scanning a multi-frame image or the
scanner doesn't know what the eventual image height
will be (common for hand-held scanners). In either
case, we need to buffer all data before we can write
the image. */
image.width = parm.pixels_per_line;
1999-08-09 18:06:01 +00:00
if (parm.lines >= 0)
/* See advance(); we allocate one extra line so we
don't end up realloc'ing in when the image has been
filled in. */
image.height = parm.lines - STRIP_HEIGHT + 1;
else
image.height = 0;
image.Bpp = 3;
if (parm.format == SANE_FRAME_GRAY)
image.Bpp = 1;
if (parm.depth == 16)
image.Bpp *= 2;
1999-08-09 18:06:01 +00:00
image.x = image.width - 1;
image.y = -1;
if (!advance (&image))
2000-08-12 15:11:46 +00:00
{
status = SANE_STATUS_NO_MEM;
goto cleanup;
}
1999-08-09 18:06:01 +00:00
}
}
else
{
assert (parm.format >= SANE_FRAME_RED
&& parm.format <= SANE_FRAME_BLUE);
offset = parm.format - SANE_FRAME_RED;
image.x = image.y = 0;
}
hundred_percent = parm.bytes_per_line * parm.lines
* ((parm.format == SANE_FRAME_RGB || parm.format == SANE_FRAME_GRAY) ? 1:3);
1999-08-09 18:06:01 +00:00
while (1)
{
double progr;
status = sane_read (device, buffer, buffer_size, &len);
total_bytes += (SANE_Word) len;
progr = ((total_bytes * 100.) / (double) hundred_percent);
if (progr > 100.)
progr = 100.;
if (progress)
fprintf (stderr, "Progress: %3.1f%%\r", progr);
1999-08-09 18:06:01 +00:00
if (status != SANE_STATUS_GOOD)
{
if (verbose && parm.depth == 8)
fprintf (stderr, "%s: min/max graylevel value = %d/%d\n",
prog_name, min, max);
if (status != SANE_STATUS_EOF)
{
fprintf (stderr, "%s: sane_read: %s\n",
prog_name, sane_strstatus (status));
return status;
1999-08-09 18:06:01 +00:00
}
break;
}
1999-08-09 18:06:01 +00:00
if (must_buffer)
{
switch (parm.format)
{
case SANE_FRAME_RED:
case SANE_FRAME_GREEN:
case SANE_FRAME_BLUE:
for (i = 0; i < len; ++i)
{
image.data[offset + 3 * i] = buffer[i];
1999-08-09 18:06:01 +00:00
if (!advance (&image))
2000-08-12 15:11:46 +00:00
{
status = SANE_STATUS_NO_MEM;
goto cleanup;
}
1999-08-09 18:06:01 +00:00
}
offset += 3 * len;
1999-08-09 18:06:01 +00:00
break;
case SANE_FRAME_RGB:
for (i = 0; i < len; ++i)
{
image.data[offset + i] = buffer[i];
if (image.Bpp == 3 || (offset + i) % 2 == 0)
if ((offset + i) % 3 == 0 && !advance (&image))
{
status = SANE_STATUS_NO_MEM;
goto cleanup;
}
1999-08-09 18:06:01 +00:00
}
offset += len;
break;
case SANE_FRAME_GRAY:
for (i = 0; i < len; ++i)
{
image.data[offset + i] = buffer[i];
if (image.Bpp == 1 || (offset + i) % 2 == 0)
if (!advance (&image))
{
status = SANE_STATUS_NO_MEM;
goto cleanup;
}
1999-08-09 18:06:01 +00:00
}
offset += len;
break;
default:
break;
1999-08-09 18:06:01 +00:00
}
}
else /* ! must_buffer */
{
if ((output_format == OUTPUT_TIFF) || (parm.depth != 16))
fwrite (buffer, 1, len, stdout);
else
{
#if !defined(WORDS_BIGENDIAN)
int i, start = 0;
/* check if we have saved one byte from the last sane_read */
if (hang_over > -1)
{
if (len > 0)
{
fwrite (buffer, 1, 1, stdout);
buffer[0] = (SANE_Byte) hang_over;
hang_over = -1;
start = 1;
}
}
/* now do the byte-swapping */
for (i = start; i < (len - 1); i += 2)
{
unsigned char LSB;
LSB = buffer[i];
buffer[i] = buffer[i + 1];
buffer[i + 1] = LSB;
}
/* check if we have an odd number of bytes */
if (((len - start) % 2) != 0)
{
hang_over = buffer[len - 1];
len--;
}
#endif
fwrite (buffer, 1, len, stdout);
}
}
1999-08-09 18:06:01 +00:00
if (verbose && parm.depth == 8)
{
for (i = 0; i < len; ++i)
if (buffer[i] >= max)
max = buffer[i];
else if (buffer[i] < min)
min = buffer[i];
}
}
first_frame = 0;
}
while (!parm.last_frame);
if (must_buffer)
{
image.height = image.y;
2000-08-12 15:11:46 +00:00
if (output_format == OUTPUT_TIFF)
sanei_write_tiff_header (parm.format, parm.pixels_per_line,
parm.lines, parm.depth, resolution_value,
icc_profile);
2000-08-12 15:11:46 +00:00
else
write_pnm_header (parm.format, image.width, image.height, parm.depth);
if ((output_format == OUTPUT_TIFF) || (image.Bpp == 1)
|| (image.Bpp == 3))
fwrite (image.data, image.Bpp, image.height * image.width, stdout);
else /* image.Bpp == 2 or image.Bpp == 6 assumed */
{
#if !defined(WORDS_BIGENDIAN)
int i;
for (i = 0; i < image.Bpp * image.height * image.width; i += 2)
{
unsigned char LSB;
LSB = image.data[i];
image.data[i] = image.data[i + 1];
image.data[i + 1] = LSB;
}
#endif
fwrite (image.data, image.Bpp, image.height * image.width, stdout);
}
1999-08-09 18:06:01 +00:00
}
/* flush the output buffer */
fflush( stdout );
1999-08-09 18:06:01 +00:00
cleanup:
if (image.data)
free (image.data);
expected_bytes = parm.bytes_per_line * parm.lines *
((parm.format == SANE_FRAME_RGB
|| parm.format == SANE_FRAME_GRAY) ? 1 : 3);
if (parm.lines < 0)
expected_bytes = 0;
if (total_bytes > expected_bytes && expected_bytes != 0)
{
fprintf (stderr,
"%s: WARNING: read more data than announced by backend "
"(%u/%u)\n", prog_name, total_bytes, expected_bytes);
}
else if (verbose)
fprintf (stderr, "%s: read %u bytes in total\n", prog_name, total_bytes);
return status;
1999-08-09 18:06:01 +00:00
}
#define clean_buffer(buf,size) memset ((buf), 0x23, size)
static void
pass_fail (int max, int len, SANE_Byte * buffer, SANE_Status status)
1999-08-09 18:06:01 +00:00
{
if (status != SANE_STATUS_GOOD)
fprintf (stderr, "FAIL Error: %s\n", sane_strstatus (status));
else if (buffer[len] != 0x23)
{
while (buffer[len] != 0x23)
++len;
1999-08-09 18:06:01 +00:00
fprintf (stderr, "FAIL Cheat: %d bytes\n", len);
}
else if (len > max)
fprintf (stderr, "FAIL Overflow: %d bytes\n", len);
else if (len == 0)
fprintf (stderr, "FAIL No data\n");
else
fprintf (stderr, "PASS\n");
}
static SANE_Status
1999-08-09 18:06:01 +00:00
test_it (void)
{
int i, len;
SANE_Parameters parm;
SANE_Status status;
Image image = { 0, 0, 0, 0, 0, 0 };
1999-08-09 18:06:01 +00:00
static const char *format_name[] =
{ "gray", "RGB", "red", "green", "blue" };
status = sane_start (device);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_start: %s\n",
prog_name, sane_strstatus (status));
goto cleanup;
}
status = sane_get_parameters (device, &parm);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_get_parameters: %s\n",
prog_name, sane_strstatus (status));
goto cleanup;
}
if (parm.lines >= 0)
fprintf (stderr, "%s: scanning image of size %dx%d pixels at "
"%d bits/pixel\n", prog_name, parm.pixels_per_line, parm.lines,
8 * parm.bytes_per_line / parm.pixels_per_line);
1999-08-09 18:06:01 +00:00
else
fprintf (stderr, "%s: scanning image %d pixels wide and "
"variable height at %d bits/pixel\n",
1999-08-09 18:06:01 +00:00
prog_name, parm.pixels_per_line,
8 * parm.bytes_per_line / parm.pixels_per_line);
fprintf (stderr, "%s: acquiring %s frame, %d bits/sample\n", prog_name,
parm.format <= SANE_FRAME_JPEG ? format_name[parm.format]:"Unknown",
parm.depth);
1999-08-09 18:06:01 +00:00
image.data = malloc (parm.bytes_per_line * 2);
1999-08-09 18:06:01 +00:00
clean_buffer (image.data, parm.bytes_per_line * 2);
fprintf (stderr, "%s: reading one scanline, %d bytes...\t", prog_name,
parm.bytes_per_line);
1999-08-09 18:06:01 +00:00
status = sane_read (device, image.data, parm.bytes_per_line, &len);
pass_fail (parm.bytes_per_line, len, image.data, status);
if (status != SANE_STATUS_GOOD)
goto cleanup;
clean_buffer (image.data, parm.bytes_per_line * 2);
fprintf (stderr, "%s: reading one byte...\t\t", prog_name);
status = sane_read (device, image.data, 1, &len);
pass_fail (1, len, image.data, status);
if (status != SANE_STATUS_GOOD)
goto cleanup;
for (i = 2; i < parm.bytes_per_line * 2; i *= 2)
1999-08-09 18:06:01 +00:00
{
clean_buffer (image.data, parm.bytes_per_line * 2);
fprintf (stderr, "%s: stepped read, %d bytes... \t", prog_name, i);
status = sane_read (device, image.data, i, &len);
pass_fail (i, len, image.data, status);
if (status != SANE_STATUS_GOOD)
goto cleanup;
}
for (i /= 2; i > 2; i /= 2)
1999-08-09 18:06:01 +00:00
{
clean_buffer (image.data, parm.bytes_per_line * 2);
fprintf (stderr, "%s: stepped read, %d bytes... \t", prog_name, i - 1);
status = sane_read (device, image.data, i - 1, &len);
pass_fail (i - 1, len, image.data, status);
if (status != SANE_STATUS_GOOD)
goto cleanup;
}
cleanup:
1999-08-09 18:06:01 +00:00
sane_cancel (device);
if (image.data)
free (image.data);
return status;
1999-08-09 18:06:01 +00:00
}
2000-08-12 15:11:46 +00:00
static int
get_resolution (void)
{
const SANE_Option_Descriptor *resopt;
int resol = 0;
void *val;
if (resolution_optind < 0)
return 0;
resopt = sane_get_option_descriptor (device, resolution_optind);
if (!resopt)
return 0;
val = alloca (resopt->size);
if (!val)
return 0;
sane_control_option (device, resolution_optind, SANE_ACTION_GET_VALUE, val,
0);
if (resopt->type == SANE_TYPE_INT)
resol = *(SANE_Int *) val;
else
resol = (int) (SANE_UNFIX (*(SANE_Fixed *) val) + 0.5);
2000-08-12 15:11:46 +00:00
return resol;
2000-08-12 15:11:46 +00:00
}
static void
scanimage_exit (void)
{
if (device)
{
if (verbose > 1)
fprintf (stderr, "Closing device\n");
sane_close (device);
}
if (verbose > 1)
fprintf (stderr, "Calling sane_exit\n");
sane_exit ();
if (all_options)
free (all_options);
if (option_number)
free (option_number);
if (verbose > 1)
fprintf (stderr, "scanimage: finished\n");
}
2000-08-12 15:11:46 +00:00
1999-08-09 18:06:01 +00:00
int
main (int argc, char **argv)
{
int ch, i, index, all_options_len;
const SANE_Option_Descriptor *opt;
const SANE_Device **device_list;
1999-08-09 18:06:01 +00:00
SANE_Int num_dev_options = 0;
const char *devname = 0;
const char *defdevname = 0;
const char *format = 0;
char readbuf[2];
char *readbuf2;
int batch = 0;
int batch_prompt = 0;
int batch_count = BATCH_COUNT_UNLIMITED;
int batch_start_at = 1;
int batch_increment = 1;
1999-08-09 18:06:01 +00:00
SANE_Status status;
char *full_optstring;
SANE_Int version_code;
1999-08-09 18:06:01 +00:00
atexit (scanimage_exit);
1999-08-09 18:06:01 +00:00
buffer_size = (32 * 1024); /* default size */
1999-08-09 18:06:01 +00:00
prog_name = strrchr (argv[0], '/');
if (prog_name)
++prog_name;
else
prog_name = argv[0];
defdevname = getenv ("SANE_DEFAULT_DEVICE");
2000-08-12 15:11:46 +00:00
sane_init (&version_code, auth_callback);
1999-08-09 18:06:01 +00:00
/* make a first pass through the options with error printing and argument
permutation disabled: */
opterr = 0;
while ((ch = getopt_long (argc, argv, "-" BASE_OPTSTRING, basic_options,
&index)) != EOF)
1999-08-09 18:06:01 +00:00
{
switch (ch)
{
case ':':
case '?':
break; /* may be an option that we'll parse later on */
case 'd':
devname = optarg;
break;
case 'b':
/* This may have already been set by the batch-count flag */
batch = 1;
format = optarg;
break;
case 'h':
help = 1;
break;
case 'i': /* icc profile */
icc_profile = optarg;
break;
case 'v':
++verbose;
break;
case 'p':
progress = 1;
break;
case 'B':
buffer_size = (1024 * 1024);
break;
case 'T':
test = 1;
break;
case 'n':
dont_scan = 1;
break;
case OPTION_BATCH_PROMPT:
batch_prompt = 1;
break;
case OPTION_BATCH_INCREMENT:
batch_increment = atoi (optarg);
break;
case OPTION_BATCH_START_AT:
batch_start_at = atoi (optarg);
break;
case OPTION_BATCH_DOUBLE:
batch_increment = 2;
break;
case OPTION_BATCH_COUNT:
batch_count = atoi (optarg);
batch = 1;
break;
2000-08-12 15:11:46 +00:00
case OPTION_FORMAT:
if (strcmp (optarg, "tiff") == 0)
output_format = OUTPUT_TIFF;
else
output_format = OUTPUT_PNM;
break;
case OPTION_MD5:
accept_only_md5_auth = 1;
break;
1999-08-09 18:06:01 +00:00
case 'L':
case 'f':
1999-08-09 18:06:01 +00:00
{
int i = 0;
1999-08-09 18:06:01 +00:00
status = sane_get_devices (&device_list, SANE_FALSE);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_get_devices() failed: %s\n",
prog_name, sane_strstatus (status));
exit (1);
}
if (ch == 'L')
{
for (i = 0; device_list[i]; ++i)
{
printf ("device `%s' is a %s %s %s\n",
device_list[i]->name, device_list[i]->vendor,
device_list[i]->model, device_list[i]->type);
}
}
else
1999-08-09 18:06:01 +00:00
{
int i = 0, int_arg = 0;
char *percent, *start, *fmt;
const char *text_arg = 0;
char cc, ftype;
fmt = malloc (strlen (optarg) + 1);
if (fmt == 0)
{
fprintf (stderr, "%s: not enough memory\n", prog_name);
exit (1);
}
for (i = 0; device_list[i]; ++i)
{
strcpy (fmt, optarg);
start = fmt;
while (*start && (percent = strchr (start, '%')))
{
percent++;
if (*percent)
{
switch (*percent)
{
case 'd':
text_arg = device_list[i]->name;
ftype = *percent = 's';
break;
case 'v':
text_arg = device_list[i]->vendor;
ftype = *percent = 's';
break;
case 'm':
text_arg = device_list[i]->model;
ftype = *percent = 's';
break;
case 't':
text_arg = device_list[i]->type;
ftype = *percent = 's';
break;
case 'i':
int_arg = i;
ftype = 'i';
break;
case '%':
ftype = 0;
break;
default:
fprintf (stderr,
"%s: unknown format specifier %%%c\n",
prog_name, *percent);
*percent = '%';
ftype = 0;
}
percent++;
cc = *percent;
*percent = 0;
switch (ftype)
{
case 's':
printf (start, text_arg);
break;
case 'i':
printf (start, int_arg);
break;
case 0:
printf (start);
break;
}
*percent = cc;
start = percent;
}
else
{
/* last char of the string is a '%', suppress it */
*start = 0;
break;
}
}
if (*start)
printf (start);
}
1999-08-09 18:06:01 +00:00
}
if (i == 0 && ch != 'f')
printf ("\nNo scanners were identified. If you were expecting "
"something different,\ncheck that the scanner is plugged "
"in, turned on and detected by the\nsane-find-scanner tool "
"(if appropriate). Please read the documentation\nwhich came "
"with this software (README, FAQ, manpages).\n");
if (defdevname)
printf ("default device is `%s'\n", defdevname);
1999-08-09 18:06:01 +00:00
exit (0);
}
case 'V':
printf ("scanimage (%s) %s; backend version %d.%d.%d\n", PACKAGE,
VERSION, SANE_VERSION_MAJOR (version_code),
SANE_VERSION_MINOR (version_code),
SANE_VERSION_BUILD (version_code));
1999-08-09 18:06:01 +00:00
exit (0);
default:
break; /* ignore device specific options for now */
1999-08-09 18:06:01 +00:00
}
}
if (help)
2005-04-28 12:49:01 +00:00
{
printf ("Usage: %s [OPTION]...\n\
1999-08-09 18:06:01 +00:00
\n\
Start image acquisition on a scanner device and write PNM image data to\n\
standard output.\n\
\n\
Parameters are separated by a blank from single-character options (e.g.\n\
-d epson) and by a \"=\" from multi-character options (e.g. --device-name=epson).\n\
-d, --device-name=DEVICE use a given scanner device (e.g. hp:/dev/scanner)\n\
--format=pnm|tiff file format of output file\n\
-i, --icc-profile=PROFILE include this ICC profile into TIFF file\n", prog_name);
2005-04-28 12:49:01 +00:00
printf ("\
-L, --list-devices show available scanner devices\n\
-f, --formatted-device-list=FORMAT similar to -L, but the FORMAT of the output\n\
can be specified: %%d (device name), %%v (vendor),\n\
%%m (model), %%t (type), and %%i (index number)\n\
-b, --batch[=FORMAT] working in batch mode, FORMAT is `out%%d.pnm' or\n\
`out%%d.tif' by default depending on --format\n");
2005-04-28 12:49:01 +00:00
printf ("\
--batch-start=# page number to start naming files with\n\
--batch-count=# how many pages to scan in batch mode\n\
--batch-increment=# increase number in filename by an amount of #\n\
--batch-double increment page number by two for 2sided originals\n\
being scanned in a single sided scanner\n\
--batch-prompt ask for pressing a key before scanning a page\n\
--accept-md5-only only accept authorization requests using md5\n");
2005-04-28 12:49:01 +00:00
printf ("\
-p, --progress print progress messages\n\
-n, --dont-scan only set options, don't actually scan\n\
1999-08-09 18:06:01 +00:00
-T, --test test backend thoroughly\n\
-h, --help display this help message and exit\n\
1999-08-09 18:06:01 +00:00
-v, --verbose give even more status messages\n\
-B, --buffer-size change default input buffersize\n\
-V, --version print version information\n");
2005-04-28 12:49:01 +00:00
}
1999-08-09 18:06:01 +00:00
if (!devname)
{
2000-08-12 15:11:46 +00:00
/* If no device name was specified explicitly, we look at the
environment variable SANE_DEFAULT_DEVICE. If this variable
is not set, we open the first device we find (if any): */
devname = defdevname;
if (!devname)
{
2000-08-12 15:11:46 +00:00
status = sane_get_devices (&device_list, SANE_FALSE);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_get_devices() failed: %s\n",
prog_name, sane_strstatus (status));
exit (1);
}
if (!device_list[0])
{
fprintf (stderr, "%s: no SANE devices found\n", prog_name);
exit (1);
}
devname = device_list[0]->name;
}
1999-08-09 18:06:01 +00:00
}
status = sane_open (devname, &device);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: open of device %s failed: %s\n",
prog_name, devname, sane_strstatus (status));
if (devname[0] == '/')
fprintf (stderr, "\nYou seem to have specified a UNIX device name, "
"or filename instead of selecting\nthe SANE scanner or "
"image acquisition device you want to use. As an example,\n"
"you might want \"epson:/dev/sg0\" or "
"\"hp:/dev/usbscanner0\". If any supported\ndevices are "
"installed in your system, you should be able to see a "
"list with\n\"scanimage --list-devices\".\n");
1999-08-09 18:06:01 +00:00
if (help)
device = 0;
else
exit (1);
}
if (device)
{
/* We got a device, find out how many options it has: */
status = sane_control_option (device, 0, SANE_ACTION_GET_VALUE,
&num_dev_options, 0);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: unable to determine option count\n",
prog_name);
exit (1);
}
all_options_len = num_dev_options + NELEMS (basic_options) + 1;
1999-08-09 18:06:01 +00:00
all_options = malloc (all_options_len * sizeof (all_options[0]));
option_number_len = num_dev_options;
option_number = malloc (option_number_len * sizeof (option_number[0]));
if (!all_options || !option_number)
{
fprintf (stderr, "%s: out of memory in fetch_options()\n",
prog_name);
exit (1);
}
fetch_options (device);
{
char *larg, *targ, *xarg, *yarg;
larg = targ = xarg = yarg = "";
/* Maybe accept t, l, x, and y options. */
if (window[0])
xarg = "x:";
if (window[1])
yarg = "y:";
if (window[2])
larg = "l:";
if (window[3])
targ = "t:";
/* Now allocate the full option list. */
full_optstring = malloc (strlen (BASE_OPTSTRING)
+ strlen (larg) + strlen (targ)
+ strlen (xarg) + strlen (yarg) + 1);
if (!full_optstring)
{
fprintf (stderr, "%s: out of memory\n", prog_name);
exit (1);
}
strcpy (full_optstring, BASE_OPTSTRING);
strcat (full_optstring, larg);
strcat (full_optstring, targ);
strcat (full_optstring, xarg);
strcat (full_optstring, yarg);
}
optind = 0;
opterr = 1; /* re-enable error printing and arg permutation */
1999-08-09 18:06:01 +00:00
while ((ch = getopt_long (argc, argv, full_optstring, all_options,
&index)) != EOF)
1999-08-09 18:06:01 +00:00
{
switch (ch)
{
case ':':
case '?':
exit (1); /* error message is printed by getopt_long() */
1999-08-09 18:06:01 +00:00
case 'd':
case 'h':
case 'p':
case 'v':
case 'V':
case 'T':
case 'B':
1999-08-09 18:06:01 +00:00
/* previously handled options */
break;
case 'x':
window_val_user[0] = 1;
parse_vector (&window_option[0], optarg, &window_val[0], 1);
break;
case 'y':
window_val_user[1] = 1;
parse_vector (&window_option[1], optarg, &window_val[1], 1);
break;
case 'l': /* tl-x */
1999-08-09 18:06:01 +00:00
process_backend_option (device, window[2], optarg);
break;
case 't': /* tl-y */
1999-08-09 18:06:01 +00:00
process_backend_option (device, window[3], optarg);
break;
case 0:
process_backend_option (device, option_number[index], optarg);
break;
}
}
if (optind < argc)
{
fprintf (stderr, "%s: argument without option: `%s'; ", prog_name,
argv[argc - 1]);
fprintf (stderr, "try %s --help\n", prog_name);
exit (1);
}
1999-08-09 18:06:01 +00:00
free (full_optstring);
for (index = 0; index < 2; ++index)
if (window[index])
{
SANE_Word val, pos;
pos = 0;
1999-08-09 18:06:01 +00:00
val = window_val[index] - 1;
1999-08-09 18:06:01 +00:00
if (window[index + 2])
{
sane_control_option (device, window[index + 2],
SANE_ACTION_GET_VALUE, &pos, 0);
val = pos + window_val[index] - 1;
}
1999-08-09 18:06:01 +00:00
set_option (device, window[index], &val);
}
if (help)
{
printf ("\nOptions specific to device `%s':\n", devname);
for (i = 0; i < num_dev_options; ++i)
{
char short_name = '\0';
int j;
opt = 0;
for (j = 0; j < 4; ++j)
if (i == window[j])
{
short_name = "xylt"[j];
if (j < 2)
opt = window_option + j;
}
if (!opt)
opt = sane_get_option_descriptor (device, i);
if (opt->type == SANE_TYPE_GROUP)
printf (" %s:\n", opt->title);
if (!SANE_OPTION_IS_SETTABLE (opt->cap))
continue;
print_option (device, i, short_name);
}
if (num_dev_options)
fputc ('\n', stdout);
}
}
if (help)
{
printf ("\
Type ``%s --help -d DEVICE'' to get list of all options for DEVICE.\n\
\n\
List of available devices:", prog_name);
status = sane_get_devices (&device_list, SANE_FALSE);
if (status == SANE_STATUS_GOOD)
{
int column = 80;
for (i = 0; device_list[i]; ++i)
{
if (column + strlen (device_list[i]->name) + 1 >= 80)
{
printf ("\n ");
column = 4;
}
if (column > 4)
{
fputc (' ', stdout);
column += 1;
}
fputs (device_list[i]->name, stdout);
column += strlen (device_list[i]->name);
}
}
fputc ('\n', stdout);
exit (0);
}
if (dont_scan)
exit (0);
2000-08-12 15:11:46 +00:00
if (output_format != OUTPUT_PNM)
resolution_value = get_resolution ();
signal (SIGHUP, sighandler);
signal (SIGINT, sighandler);
1999-08-09 18:06:01 +00:00
signal (SIGPIPE, sighandler);
signal (SIGTERM, sighandler);
if (test == 0)
{
int n = batch_start_at;
if (batch && NULL == format)
{
if (output_format == OUTPUT_TIFF)
format = "out%d.tif";
else
format = "out%d.pnm";
}
if (batch)
fprintf (stderr,
"Scanning %d pages, incrementing by %d, numbering from %d\n",
batch_count, batch_increment, batch_start_at);
do
{
char path[PATH_MAX];
if (batch) /* format is NULL unless batch mode */
sprintf (path, format, n); /* love --(C++) */
if (batch)
{
if (batch_prompt)
{
fprintf (stderr, "Place document no. %d on the scanner.\n",
n);
fprintf (stderr, "Press <RETURN> to continue.\n");
fprintf (stderr, "Press Ctrl + D to terminate.\n");
readbuf2 = fgets (readbuf, 2, stdin);
if (readbuf2 == NULL)
{
fprintf (stderr, "Batch terminated, %d pages scanned\n",
(n - batch_increment));
fclose (stdout);
break; /* get out of this loop */
}
}
fprintf (stderr, "Scanning page %d\n", n);
}
status = sane_start (device);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "%s: sane_start: %s\n",
prog_name, sane_strstatus (status));
fclose (stdout);
break;
}
if (batch && NULL == freopen (path, "w", stdout))
{
fprintf (stderr, "cannot open %s\n", path);
sane_cancel (device);
return SANE_STATUS_ACCESS_DENIED;
}
status = scan_it ();
if (batch)
{
fprintf (stderr, "Scanned page %d.", n);
fprintf (stderr, " (scanner status = %d)\n", status);
}
switch (status)
{
case SANE_STATUS_GOOD:
break;
case SANE_STATUS_EOF:
status = SANE_STATUS_GOOD;
break;
default:
if (batch)
{
fclose (stdout);
unlink (path);
}
break;
} /* switch */
n += batch_increment;
}
while ((batch
&& (batch_count == BATCH_COUNT_UNLIMITED || --batch_count))
&& SANE_STATUS_GOOD == status);
sane_cancel (device);
}
1999-08-09 18:06:01 +00:00
else
status = test_it ();
1999-08-09 18:06:01 +00:00
return status;
1999-08-09 18:06:01 +00:00
}