sane-project-backends/tools/sane-desc.c

3741 wiersze
96 KiB
C
Czysty Wina Historia

/*
sane-desc.c -- generate list of supported SANE devices
Copyright (C) 2002-2006 Henning Meier-Geinitz <henning@meier-geinitz.de>
Copyright (C) 2004 Jose Gato <jgato@gsyc.escet.urjc.es> (XML output)
Copyright (C) 2006 Mattias Ellert <mattias.ellert@tsl.uu.se> (plist output)
Copyright (C) 2009 Dr. Ing. Dieter Jurzitza <dieter.jurzitza@t-online.de>
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.
*/
#include <../include/sane/config.h>
#include "../include/getopt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <ctype.h>
#include <time.h>
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/sanei_config.h"
#define SANE_DESC_VERSION "3.5"
#define MAN_PAGE_LINK "http://www.sane-project.org/man/%s.5.html"
#define COLOR_MINIMAL "\"#B00000\""
#define COLOR_BASIC "\"#FF9000\""
#define COLOR_GOOD "\"#90B000\""
#define COLOR_COMPLETE "\"#007000\""
#define COLOR_UNTESTED "\"#0000B0\""
#define COLOR_UNSUPPORTED "\"#F00000\""
#define COLOR_NEW "\"#F00000\""
#define COLOR_UNKNOWN "\"#000000\""
#define DEVMODE "0664"
#define DEVOWNER "root"
#define DEVGROUP "scanner"
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif
#define DBG_ERR current_debug_level = 0; debug_call
#define DBG_WARN current_debug_level = 1; debug_call
#define DBG_INFO current_debug_level = 2; debug_call
#define DBG_DBG current_debug_level = 3; debug_call
typedef enum output_mode
{
output_mode_ascii = 0,
output_mode_xml,
output_mode_html_backends,
output_mode_html_backends_split,
output_mode_html_mfgs,
output_mode_statistics,
output_mode_usermap,
output_mode_db,
output_mode_udev,
output_mode_plist,
output_mode_hal,
output_mode_halnew
}
output_mode;
typedef enum parameter_type
{
param_none = 0,
param_string,
param_two_strings,
param_three_strings
}
parameter_type;
typedef enum status_entry
{
status_unknown,
status_unsupported,
status_untested,
status_minimal,
status_basic,
status_good,
status_complete
}
status_entry;
typedef enum device_type
{
type_unknown,
type_scanner,
type_stillcam,
type_vidcam,
type_meta,
type_api
}
device_type;
typedef enum level
{
level_backend,
level_mfg,
level_model,
level_desc
}
level;
typedef struct url_entry
{
struct url_entry *next;
char *name;
}
url_entry;
typedef struct model_entry
{
struct model_entry *next;
char *name;
char *interface;
struct url_entry *url;
char *comment;
enum status_entry status;
char *usb_vendor_id;
char *usb_product_id;
SANE_Bool ignore_usb_id;
char *scsi_vendor_id;
char *scsi_product_id;
SANE_Bool scsi_is_processor;
}
model_entry;
typedef struct desc_entry
{
struct desc_entry *next;
char *desc;
struct url_entry *url;
char *comment;
}
desc_entry;
typedef struct mfg_entry
{
struct mfg_entry *next;
char *name;
struct url_entry *url;
char *comment;
struct model_entry *model;
}
mfg_entry;
typedef struct type_entry
{
struct type_entry *next;
enum device_type type;
struct desc_entry *desc;
struct mfg_entry *mfg;
}
type_entry;
typedef struct backend_entry
{
struct backend_entry *next;
char *name;
char *version;
char *manpage;
struct url_entry *url;
char *comment;
struct type_entry *type;
SANE_Bool new;
}
backend_entry;
typedef struct model_record_entry
{
struct model_record_entry *next;
char *name;
char *interface;
struct url_entry *url;
char *comment;
enum status_entry status;
char *usb_vendor_id;
char *usb_product_id;
char *scsi_vendor_id;
char *scsi_product_id;
SANE_Bool scsi_is_processor;
struct backend_entry *be;
}
model_record_entry;
typedef struct mfg_record_entry
{
struct mfg_record_entry *next;
char *name;
char *comment;
struct url_entry *url;
struct model_record_entry *model_record;
}
mfg_record_entry;
typedef int statistics_type [status_complete + 1];
typedef struct manufacturer_model_type
{
struct manufacturer_model_type * next;
char *name;
}
manufacturer_model_type;
typedef struct usbid_type
{
struct usbid_type * next;
char *usb_vendor_id;
char *usb_product_id;
struct manufacturer_model_type *name;
}
usbid_type;
typedef struct scsiid_type
{
struct scsiid_type * next;
char *scsi_vendor_id;
char *scsi_product_id;
SANE_Bool is_processor;
struct manufacturer_model_type *name;
}
scsiid_type;
static char *program_name;
static int debug = 0;
static int current_debug_level = 0;
static char *search_dir_spec = 0;
static backend_entry *first_backend = 0;
static enum output_mode mode = output_mode_ascii;
static char *title = 0;
static char *intro = 0;
static SANE_String desc_name = 0;
static const char *status_name[] =
{"Unknown", "Unsupported", "Untested", "Minimal", "Basic",
"Good", "Complete"};
static const char *device_type_name[] =
{"Unknown", "Scanners", "Still cameras", "Video Cameras", "Meta backends",
"APIs"};
static const char *device_type_aname[] =
{"UKNOWN", "SCANNERS", "STILL", "VIDEO", "META",
"API"};
static const char *status_color[] =
{COLOR_UNKNOWN, COLOR_UNSUPPORTED, COLOR_UNTESTED, COLOR_MINIMAL,
COLOR_BASIC, COLOR_GOOD, COLOR_COMPLETE};
static void
debug_call (const char *fmt, ...)
{
va_list ap;
char *level_txt;
va_start (ap, fmt);
if (debug >= current_debug_level)
{
/* print to stderr */
switch (current_debug_level)
{
case 0:
level_txt = "ERROR:";
break;
case 1:
level_txt = "Warning:";
break;
case 2:
level_txt = "Info:";
break;
default:
level_txt = "";
break;
}
if (desc_name)
fprintf (stderr, "%s: %8s ", desc_name, level_txt);
else
fprintf (stderr, "[%s] %8s ", program_name, level_txt);
vfprintf (stderr, fmt, ap);
}
va_end (ap);
}
static void
print_usage (char *program_name)
{
printf ("Usage: %s [-s dir] [-m mode] [-d level] [-h] [-V]\n",
program_name);
printf (" -s|--search-dir dir "
"Specify the directory that contains .desc files \n"
" "
"(multiple directories can be concatenated by \":\")\n");
printf (" -m|--mode mode "
"Output mode (ascii, html-backends-split, html-mfgs,\n"
" xml, statistics, usermap, db, udev, plist, hal, hal-new)\n");
printf (" -t|--title \"title\" The title used for HTML pages\n");
printf (" -i|--intro \"intro\" A short description of the "
"contents of the page\n");
printf (" -d|--debug-level level Specify debug level (0-3)\n");
printf (" -h|--help Print help message\n");
printf (" -V|--version Print version information\n");
printf ("Report bugs to <henning@meier-geinitz.de>\n");
}
static void
print_version (void)
{
printf ("sane-desc %s (%s)\n", SANE_DESC_VERSION, PACKAGE_STRING);
printf ("Copyright (C) 2002-2006 Henning Meier-Geinitz "
"<henning@meier-geinitz.de>\n"
"sane-desc comes with NO WARRANTY, to the extent permitted by "
"law.\n"
"You may redistribute copies of sane-desc under the terms of the "
"GNU General\n"
"Public License.\n"
"For more information about these matters, see the file named "
"COPYING.\n");
}
static SANE_Bool
get_options (int argc, char **argv)
{
int longindex;
int opt;
static struct option desc_options[] = {
{"search-dir", required_argument, NULL, 's'},
{"mode", required_argument, NULL, 'm'},
{"title", required_argument, NULL, 't'},
{"intro", required_argument, NULL, 'i'},
{"debug-level", required_argument, NULL, 'd'},
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{0, 0, 0, 0}
};
while ((opt = getopt_long (argc, argv, "s:m:t:i:d:hV", desc_options,
&longindex)) != -1)
{
switch (opt)
{
case 'h':
print_usage (argv[0]);
exit (0);
case 'V':
print_version ();
exit (0);
case 's':
search_dir_spec = strdup (optarg);
DBG_INFO ("setting search directory to `%s'\n", search_dir_spec);
break;
case 'm':
if (strcmp (optarg, "ascii") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_ascii;
}
else if (strcmp (optarg, "xml") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_xml;
}
else if (strcmp (optarg, "html-backends-split") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_html_backends_split;
}
else if (strcmp (optarg, "html-mfgs") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_html_mfgs;
}
else if (strcmp (optarg, "statistics") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_statistics;
}
else if (strcmp (optarg, "usermap") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_usermap;
}
else if (strcmp (optarg, "db") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_db;
}
else if (strcmp (optarg, "udev") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_udev;
}
else if (strcmp (optarg, "plist") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_plist;
}
else if (strcmp (optarg, "hal") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_hal;
}
else if (strcmp (optarg, "hal-new") == 0)
{
DBG_INFO ("Output mode: %s\n", optarg);
mode = output_mode_halnew;
}
else
{
DBG_ERR ("Unknown output mode: %s\n", optarg);
exit (1);
}
break;
case 't':
title = optarg;
DBG_INFO ("setting title to `%s'\n", optarg);
break;
case 'i':
intro = optarg;
DBG_INFO ("setting intro to `%s'\n", optarg);
break;
case 'd':
debug = atoi (optarg);
DBG_INFO ("setting debug level to %d\n", debug);
break;
case '?':
DBG_ERR ("unknown option (use -h for help)\n");
return SANE_FALSE;
case ':':
DBG_ERR ("missing parameter (use -h for help)\n");
return SANE_FALSE;
default:
DBG_ERR ("missing option (use -h for help)\n");
return SANE_FALSE;
}
}
if (!search_dir_spec)
search_dir_spec = ".";
return SANE_TRUE;
}
static int
char_compare (char char1, char char2)
{
char1 = toupper (char1);
char2 = toupper (char2);
if (char1 < char2)
return -1;
else if (char1 > char2)
return 1;
else
return 0;
}
static int
num_compare (char *num_string1, char *num_string2)
{
int num1 = atoi (num_string1);
int num2 = atoi (num_string2);
if (num1 < num2)
return -1;
else if (num1 > num2)
return 1;
else
return 0;
}
/* Compare two strings, try to sort numbers correctly (600 < 1200) */
static int
string_compare (char *string1, char *string2)
{
int count = 0;
int compare = 0;
if (!string1)
{
if (!string2)
return 0;
else
return 1;
}
else if (!string2)
return -1;
while (string1[count] && string2[count])
{
if (isdigit (string1[count]) && isdigit (string2[count]))
compare = num_compare (&string1[count], &string2[count]);
else
compare = char_compare (string1[count], string2[count]);
if (compare != 0)
return compare;
count++;
}
return char_compare (string1[count], string2[count]);
}
/* Add URLs to the end of the list if they are unique */
static url_entry *
update_url_list (url_entry * first_url, char *new_url)
{
url_entry *url = first_url;
SANE_Bool found = SANE_FALSE;
while (url && url->name)
{
if (string_compare (url->name, new_url) == 0)
found = SANE_TRUE;
url = url->next;
}
if (found)
return first_url;
url = first_url;
if (url)
{
while (url->next)
url = url->next;
url->next = calloc (1, sizeof (url_entry));
url = url->next;
}
else
{
first_url = calloc (1, sizeof (url_entry));
url = first_url;
}
if (!url)
{
DBG_ERR ("update_url_list: couldn't calloc url_entry\n");
exit (1);
}
url->name = new_url;
return first_url;
}
/* Get the next token, ignoring escaped quotation marks */
static const char *
get_token (const char *str, char **string_const)
{
const char *start;
size_t len;
str = sanei_config_skip_whitespace (str);
if (*str == '"')
{
start = ++str;
while (*str && (*str != '"' || *(str - 1) == '\\'))
++str;
len = str - start;
if (*str == '"')
++str;
else
start = 0; /* final double quote is missing */
}
else
{
start = str;
while (*str && !isspace (*str))
++str;
len = str - start;
}
if (start)
*string_const = strndup (start, len);
else
*string_const = NULL;
return str;
}
/* Checks a line for a keyword token and determines keyword/string argument */
static SANE_Status
read_keyword (SANE_String line, SANE_String keyword_token,
parameter_type p_type, void *argument)
{
SANE_String_Const cp;
SANE_Char *word;
word = 0;
cp = get_token (line, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
if (strcmp (word, keyword_token) != 0)
return SANE_STATUS_INVAL;
free (word);
word = 0;
switch (p_type)
{
case param_none:
return SANE_STATUS_GOOD;
case param_string:
{
char *pos;
cp = get_token (cp, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
/* remove escaped quotations */
while ((pos = strstr (word, "\\\"")) != 0)
*pos = ' ';
DBG_DBG ("read_keyword: set entry `%s' to `%s'\n", keyword_token,
word);
*(SANE_String *) argument = strdup (word);
break;
}
case param_two_strings:
{
char *pos;
char **strings = malloc (2 * sizeof (SANE_String));
cp = get_token (cp, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
/* remove escaped quotations */
while ((pos = strstr (word, "\\\"")) != 0)
*pos = ' ';
DBG_INFO ("read_keyword: set first entry of `%s' to `%s'\n", keyword_token,
word);
strings[0] = strdup (word);
if (word)
free (word);
cp = get_token (cp, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
/* remove escaped quotations */
while ((pos = strstr (word, "\\\"")) != 0)
*pos = ' ';
DBG_INFO ("read_keyword: set second entry of `%s' to `%s'\n", keyword_token,
word);
strings[1] = strdup (word);
* (SANE_String **) argument = strings;
break;
}
case param_three_strings:
{
char *pos;
char **strings = malloc (3 * sizeof (SANE_String));
cp = get_token (cp, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
/* remove escaped quotations */
while ((pos = strstr (word, "\\\"")) != 0)
*pos = ' ';
DBG_INFO ("read_keyword: set first entry of `%s' to `%s'\n", keyword_token,
word);
strings[0] = strdup (word);
if (word)
free (word);
cp = get_token (cp, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
/* remove escaped quotations */
while ((pos = strstr (word, "\\\"")) != 0)
*pos = ' ';
DBG_INFO ("read_keyword: set second entry of `%s' to `%s'\n", keyword_token,
word);
strings[1] = strdup (word);
if (word)
free (word);
cp = get_token (cp, &word);
if (!word)
{
DBG_ERR ("read_keyword: missing quotation mark: %s\n", line);
return SANE_STATUS_INVAL;
}
/* remove escaped quotations */
while ((pos = strstr (word, "\\\"")) != 0)
*pos = ' ';
DBG_INFO ("read_keyword: set third entry of `%s' to `%s'\n", keyword_token,
word);
strings[2] = strdup (word);
* (SANE_String **) argument = strings;
break;
}
default:
DBG_ERR ("read_keyword: unknown param_type %d\n", p_type);
return SANE_STATUS_INVAL;
}
if (word)
free (word);
word = 0;
return SANE_STATUS_GOOD;
}
/* Check for a all-lowercase 4-digit hex number (e.g. 0x1234) */
static SANE_Bool
check_hex (SANE_String string)
{
unsigned int i;
if (strlen (string) != 6)
return SANE_FALSE;
if (strncmp (string, "0x", 2) != 0)
return SANE_FALSE;
for (i = 0; i < strlen (string); i++)
{
if (isupper (string[i]))
return SANE_FALSE;
}
for (i = 2; i < strlen (string); i++)
{
if (!isxdigit (string[i]))
return SANE_FALSE;
}
return SANE_TRUE;
}
/* Read and interprete the .desc files */
static SANE_Bool
read_files (void)
{
struct stat stat_buf;
DIR *dir;
struct dirent *dir_entry;
FILE *fp;
char file_name[PATH_MAX];
SANE_Char line[4096], *word;
SANE_String_Const cp;
backend_entry *current_backend = 0;
type_entry *current_type = 0;
mfg_entry *current_mfg = 0;
model_entry *current_model = 0;
enum level current_level = level_backend;
char *search_dir = search_dir_spec, *end = 0;
DBG_INFO ("looking for .desc files in `%s'\n", search_dir_spec);
while (search_dir && search_dir[0])
{
end = strchr (search_dir, ':');
if (end)
end[0] = '\0';
DBG_INFO ("reading directory `%s'\n", search_dir);
if (stat (search_dir, &stat_buf) < 0)
{
DBG_ERR ("cannot stat `%s' (%s)\n", search_dir, strerror (errno));
return SANE_FALSE;
}
if (!S_ISDIR (stat_buf.st_mode))
{
DBG_ERR ("`%s' is not a directory\n", search_dir);
return SANE_FALSE;
}
if ((dir = opendir (search_dir)) == 0)
{
DBG_ERR ("cannot read directory `%s' (%s)\n", search_dir,
strerror (errno));
return SANE_FALSE;
}
while ((dir_entry = readdir (dir)) != NULL)
{
if (strlen (dir_entry->d_name) > 5 &&
strcmp (dir_entry->d_name + strlen (dir_entry->d_name) - 5,
".desc") == 0)
{
if (strlen (search_dir)
+ strlen (dir_entry->d_name) + 1 + 1 > PATH_MAX)
{
DBG_ERR ("filename too long\n");
return SANE_FALSE;
}
sprintf (file_name, "%s/%s", search_dir, dir_entry->d_name);
DBG_INFO ("-> reading desc file: %s\n", file_name);
fp = fopen (file_name, "r");
if (!fp)
{
DBG_ERR ("can't open desc file: %s (%s)\n", file_name,
strerror (errno));
return SANE_FALSE;
}
/* now we check if everything is ok with the previous backend
before we read the new one */
if (current_backend)
{
type_entry *current_type = current_backend->type;
int no_usbids = 0;
while (current_type)
{
if (current_type->type == type_scanner ||
current_type->type == type_stillcam ||
current_type->type == type_vidcam)
{
mfg_entry *current_mfg = current_type->mfg;
while (current_mfg)
{
model_entry *current_model = current_mfg->model;
while (current_model)
{
if (current_model->status == status_unknown)
{
DBG_WARN
("Backend `%s': `%s' `%s' does not have a status\n",
current_backend->name,
current_mfg->name,
current_model->name);
}
if (!current_model->interface)
{
DBG_WARN
("Backend `%s': `%s' `%s' does not have an interface\n",
current_backend->name,
current_mfg->name,
current_model->name);
}
else if (strstr (current_model->interface, "USB"))
{
if ((!current_model->usb_vendor_id || !current_model->usb_product_id)
&& !current_model->ignore_usb_id)
{
DBG_INFO ("`%s' seems to provide a USB device "
"without :usbid (%s %s)\n",
current_backend->name,
current_mfg->name,
current_model->name);
no_usbids++;
}
}
current_model = current_model->next;
}
current_mfg = current_mfg->next;
}
}
current_type = current_type->next;
}
if (no_usbids)
{
DBG_WARN ("Backend `%s': %d USB devices without :usbid\n",
current_backend->name, no_usbids);
}
}
desc_name = dir_entry->d_name;
current_backend = 0;
current_type = 0;
current_mfg = 0;
current_model = 0;
while (sanei_config_read (line, sizeof (line), fp))
{
char *string_entry = 0;
char **two_string_entry;
char **three_string_entry;
word = 0;
cp = get_token (line, &word);
if (!word || cp == line)
{
DBG_DBG ("ignoring empty line\n");
if (word)
free (word);
word = 0;
continue;
}
if (word[0] == ';')
{
DBG_DBG ("ignoring comment line\n");
free (word);
word = 0;
continue;
}
DBG_DBG ("line: %s\n", line);
if (read_keyword
(line, ":backend", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
backend_entry *be = first_backend, *prev_be =
0, *new_be = 0;
DBG_INFO ("creating backend entry `%s'\n",
string_entry);
new_be = calloc (1, sizeof (backend_entry));
if (!new_be)
{
DBG_ERR ("calloc failed (%s)\n", strerror (errno));
return SANE_FALSE;
}
new_be->name = string_entry;
new_be->new = SANE_FALSE;
if (!be)
{
first_backend = new_be;
be = new_be;
}
else
{
while (be)
{
int compare =
string_compare (new_be->name, be->name);
if (compare <= 0)
{
backend_entry *be_tmp = be;
be = new_be;
be->next = be_tmp;
if (!prev_be)
first_backend = be;
else
prev_be->next = be;
break;
}
prev_be = be;
be = be->next;
}
if (!be) /* last entry */
{
prev_be->next = new_be;
be = prev_be->next;
}
}
current_backend = be;
current_type = 0;
current_mfg = 0;
current_model = 0;
current_level = level_backend;
continue;
}
if (!current_backend)
{
DBG_ERR ("use `:backend' keyword first\n");
return SANE_FALSE;
}
if (read_keyword
(line, ":version", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
if (current_backend->version)
{
DBG_WARN
("overwriting version of backend `%s' to `%s'"
"(was: `%s')\n", current_backend->name,
string_entry, current_backend->version,
current_backend->version);
}
DBG_INFO ("setting version of backend `%s' to `%s'\n",
current_backend->name, string_entry);
current_backend->version = string_entry;
continue;
}
if (read_keyword
(line, ":status", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
switch (current_level)
{
case level_model:
if (current_model->status != status_unknown)
{
DBG_WARN
("overwriting status of model `%s' (backend `%s')\n",
current_model->name, current_backend->name);
}
if (strcmp (string_entry, ":minimal") == 0)
{
DBG_INFO
("setting status of model `%s' to `minimal'\n",
current_model->name);
current_model->status = status_minimal;
}
else if (strcmp (string_entry, ":basic") == 0)
{
DBG_INFO
("setting status of model `%s' to `basic'\n",
current_model->name);
current_model->status = status_basic;
}
else if (strcmp (string_entry, ":good") == 0)
{
DBG_INFO
("setting status of model `%s' to `good'\n",
current_model->name);
current_model->status = status_good;
}
else if (strcmp (string_entry, ":complete") == 0)
{
DBG_INFO
("setting status of model `%s' to `complete'\n",
current_model->name);
current_model->status = status_complete;
}
else if (strcmp (string_entry, ":untested") == 0)
{
DBG_INFO
("setting status of model `%s' to `untested'\n",
current_model->name);
current_model->status = status_untested;
}
else if (strcmp (string_entry, ":unsupported") == 0)
{
DBG_INFO
("setting status of model `%s' to `unsupported'\n",
current_model->name);
current_model->status = status_unsupported;
}
else
{
DBG_ERR
("unknown status of model `%s': `%s' (backend `%s')\n",
current_model->name, string_entry,
current_backend->name);
current_model->status = status_untested;
return SANE_FALSE;
}
break;
default:
DBG_ERR
("level %d not implemented for :status (backend `%s')\n",
current_level, current_backend->name);
return SANE_FALSE;
}
continue;
}
if (read_keyword (line, ":new", param_string, &string_entry)
== SANE_STATUS_GOOD)
{
if (strcmp (string_entry, ":yes") == 0)
{
DBG_INFO
("backend %s is new in this SANE release\n",
current_backend->name);
current_backend->new = SANE_TRUE;
}
else if (strcmp (string_entry, ":no") == 0)
{
DBG_INFO
("backend %s is NOT new in this SANE release\n",
current_backend->name);
current_backend->new = SANE_FALSE;
}
else
{
DBG_ERR ("unknown :new parameter of backend `%s': "
"`%s'\n", current_backend->name,
string_entry);
current_backend->new = SANE_FALSE;
return SANE_FALSE;
}
continue;
}
if (read_keyword
(line, ":manpage", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
if (current_backend->manpage)
{
DBG_WARN
("overwriting manpage of backend `%s' to `%s'"
"(was: `%s')\n", current_backend->name,
string_entry, current_backend->manpage);
}
DBG_INFO ("setting manpage of backend `%s' to `%s'\n",
current_backend->name, string_entry);
current_backend->manpage = string_entry;
continue;
}
if (read_keyword
(line, ":devicetype", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
type_entry *type = 0;
type = current_backend->type;
DBG_INFO
("adding `%s' to list of device types of backend "
"`%s'\n", string_entry, current_backend->name);
if (type)
{
while (type->next)
type = type->next;
type->next = calloc (1, sizeof (type_entry));
type = type->next;
}
else
{
current_backend->type =
calloc (1, sizeof (type_entry));
type = current_backend->type;
}
type->type = type_unknown;
if (strcmp (string_entry, ":scanner") == 0)
{
DBG_INFO ("setting device type of backend `%s' to "
"scanner\n", current_backend->name);
type->type = type_scanner;
}
else if (strcmp (string_entry, ":stillcam") == 0)
{
DBG_INFO ("setting device type of backend `%s' to "
"still camera\n", current_backend->name);
type->type = type_stillcam;
}
else if (strcmp (string_entry, ":vidcam") == 0)
{
DBG_INFO ("setting device type of backend `%s' to "
"video camera\n", current_backend->name);
type->type = type_vidcam;
}
else if (strcmp (string_entry, ":api") == 0)
{
DBG_INFO ("setting device type of backend `%s' to "
"API\n", current_backend->name);
type->type = type_api;
}
else if (strcmp (string_entry, ":meta") == 0)
{
DBG_INFO ("setting device type of backend `%s' to "
"meta\n", current_backend->name);
type->type = type_meta;
}
else
{
DBG_ERR
("unknown device type of backend `%s': `%s'\n",
current_backend->name, string_entry);
type->type = type_unknown;
return SANE_FALSE;
}
current_type = type;
current_mfg = 0;
current_model = 0;
continue;
}
if (read_keyword
(line, ":desc", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
if (!current_type)
{
DBG_ERR
("use `:devicetype' keyword first (backend `%s')\n",
current_backend->name);
return SANE_FALSE;
}
if (current_type->type < type_meta)
{
DBG_ERR
("use `:desc' for `:api' and `:meta' only (backend `%s')\n",
current_backend->name);
return SANE_FALSE;
}
if (current_type->desc)
{
DBG_WARN
("overwriting description of device type of "
"backend `%s' to `%s' (was: `%s')\n",
current_backend->name, string_entry,
current_type->desc);
}
DBG_INFO
("setting description of backend `%s' to `%s'\n",
current_backend->name, string_entry);
current_type->desc = calloc (1, sizeof (desc_entry));
if (!current_type->desc)
{
DBG_ERR ("calloc failed (%s)\n", strerror (errno));
return SANE_FALSE;
}
current_type->desc->desc = string_entry;
current_level = level_desc;
current_mfg = 0;
current_model = 0;
continue;
}
if (read_keyword (line, ":mfg", param_string, &string_entry)
== SANE_STATUS_GOOD)
{
mfg_entry *mfg = 0;
if (!current_type)
{
DBG_ERR
("use `:devicetype' keyword first (backend `%s')\n",
current_backend->name);
return SANE_FALSE;
}
if (current_type->type >= type_meta)
{
DBG_ERR
("use `:mfg' for hardware devices only (backend `%s')\n",
current_backend->name);
return SANE_FALSE;
}
mfg = current_type->mfg;
if (mfg)
{
while (mfg->next)
mfg = mfg->next;
mfg->next = calloc (1, sizeof (mfg_entry));
mfg = mfg->next;
}
else
{
current_type->mfg = calloc (1, sizeof (mfg_entry));
mfg = current_type->mfg;
}
if (!mfg)
{
DBG_ERR ("calloc failed (%s)\n", strerror (errno));
return SANE_FALSE;
}
mfg->name = string_entry;
DBG_INFO ("adding mfg entry %s to backend `%s'\n",
string_entry, current_backend->name);
current_mfg = mfg;
current_model = 0;
current_level = level_mfg;
continue;
}
if (read_keyword
(line, ":model", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
model_entry *model = 0;
if (!current_type)
{
DBG_ERR
("use `:devicetype' keyword first (backend `%s')\n",
current_backend->name);
return SANE_FALSE;
}
if (current_level != level_mfg
&& current_level != level_model)
{
DBG_ERR
("use `:mfg' keyword first (backend `%s')\n",
current_backend->name);
return SANE_FALSE;
}
model = current_mfg->model;
if (model)
{
while (model->next)
model = model->next;
model->next = calloc (1, sizeof (model_entry));
model = model->next;
}
else
{
current_mfg->model =
calloc (1, sizeof (model_entry));
model = current_mfg->model;
}
if (!model)
{
DBG_ERR ("calloc failed (%s)\n", strerror (errno));
return SANE_FALSE;
}
model->name = string_entry;
model->status = status_unknown;
DBG_INFO
("adding model entry %s to manufacturer `%s'\n",
string_entry, current_mfg->name);
current_model = model;
current_level = level_model;
continue;
}
if (read_keyword
(line, ":interface", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
if (!current_model)
{
DBG_WARN
("ignored `%s' :interface, only allowed for "
"hardware devices\n", current_backend->name);
continue;
}
if (current_model->interface)
{
DBG_WARN ("overwriting `%s's interface of model "
"`%s' to `%s' (was: `%s')\n",
current_backend->name,
current_model->name, string_entry,
current_model->interface);
}
DBG_INFO ("setting interface of model `%s' to `%s'\n",
current_model->name, string_entry);
current_model->interface = string_entry;
continue;
}
if (read_keyword
(line, ":scsi", param_three_strings,
&three_string_entry) == SANE_STATUS_GOOD)
{
if (!current_model)
{
DBG_WARN
("ignored `%s' :scsi, only allowed for "
"hardware devices\n", current_backend->name);
continue;
}
DBG_INFO ("setting scsi vendor and product ids of model `%s' to `%s/%s'\n",
current_model->name, three_string_entry[0], three_string_entry[1]);
if (strcasecmp (three_string_entry[0], "ignore") == 0)
{
DBG_INFO ("Ignoring `%s's scsi-entries of `%s'\n",
current_backend->name,
current_model->name);
continue;
}
if (strcasecmp (three_string_entry[2], "processor") == 0){
current_model->scsi_is_processor = SANE_TRUE;
current_model->scsi_vendor_id = three_string_entry[0];
current_model->scsi_product_id = three_string_entry[1];
}
else
{
DBG_INFO ("scsi-format info in %s is invalid -> break\n", current_backend->name);
continue;
}
continue;
}
if (read_keyword
(line, ":usbid", param_two_strings,
&two_string_entry) == SANE_STATUS_GOOD)
{
if (!current_model)
{
DBG_WARN
("ignored `%s' :usbid, only allowed for "
"hardware devices\n", current_backend->name);
continue;
}
if (strcasecmp (two_string_entry[0], "ignore") == 0)
{
DBG_INFO ("Ignoring `%s's USB ids of `%s'\n",
current_backend->name,
current_model->name);
current_model->ignore_usb_id = SANE_TRUE;
continue;
}
if (!check_hex (two_string_entry[0]))
{
DBG_WARN ("`%s's USB vendor id of `%s' is "
"not a lowercase 4-digit hex number: "
"`%s'\n", current_backend->name,
current_model->name, two_string_entry[0]);
continue;
}
if (!check_hex (two_string_entry[1]))
{
DBG_WARN ("`%s's USB product id of `%s' is "
"not a lowercase 4-digit hex number: "
"`%s'\n", current_backend->name,
current_model->name, two_string_entry[1]);
continue;
}
if (current_model->usb_vendor_id || current_model->usb_product_id)
{
DBG_WARN ("overwriting `%s's USB ids of model "
"`%s' to `%s/%s' (was: `%s/%s')\n",
current_backend->name,
current_model->name, two_string_entry[0],
two_string_entry[1],
current_model->usb_vendor_id,
current_model->usb_product_id);
}
DBG_INFO ("setting USB vendor and product ids of model `%s' to `%s/%s'\n",
current_model->name, two_string_entry[0], two_string_entry[1]);
current_model->usb_vendor_id = two_string_entry[0];
current_model->usb_product_id = two_string_entry[1];
continue;
}
if (read_keyword (line, ":url", param_string, &string_entry)
== SANE_STATUS_GOOD)
{
switch (current_level)
{
case level_backend:
current_backend->url =
update_url_list (current_backend->url,
string_entry);
DBG_INFO ("adding `%s' to list of urls of backend "
"`%s'\n", string_entry,
current_backend->name);
break;
case level_mfg:
current_mfg->url =
update_url_list (current_mfg->url, string_entry);
DBG_INFO ("adding `%s' to list of urls of mfg "
"`%s'\n", string_entry,
current_mfg->name);
break;
case level_desc:
current_type->desc->url =
update_url_list (current_type->desc->url,
string_entry);
DBG_INFO
("adding `%s' to list of urls of description "
"for backend `%s'\n", string_entry,
current_backend->name);
break;
case level_model:
current_model->url =
update_url_list (current_model->url,
string_entry);
DBG_INFO ("adding `%s' to list of urls of model "
"`%s'\n", string_entry,
current_model->name);
break;
default:
DBG_ERR
("level %d not implemented for :url (backend `%s')\n",
current_level, current_backend->name);
return SANE_FALSE;
}
continue;
}
if (read_keyword
(line, ":comment", param_string,
&string_entry) == SANE_STATUS_GOOD)
{
switch (current_level)
{
case level_backend:
current_backend->comment = string_entry;
DBG_INFO ("setting comment of backend %s to `%s'\n",
current_backend->name, string_entry);
break;
case level_mfg:
current_mfg->comment = string_entry;
DBG_INFO
("setting comment of manufacturer %s to `%s'\n",
current_mfg->name, string_entry);
break;
case level_desc:
current_type->desc->comment = string_entry;
DBG_INFO ("setting comment of description for "
"backend %s to `%s'\n",
current_backend->name, string_entry);
break;
case level_model:
current_model->comment = string_entry;
DBG_INFO ("setting comment of model %s to `%s'\n",
current_model->name, string_entry);
break;
default:
DBG_ERR
("level %d not implemented for `:comment' (backend `%s')\n",
current_level, current_backend->name);
return SANE_FALSE;
}
continue;
}
DBG_ERR
("unknown keyword token in line `%s' of file `%s'\n",
line, file_name);
return SANE_FALSE;
} /* while (sanei_config_readline) */
fclose (fp);
} /* if (strlen) */
} /* while (direntry) */
if (end)
search_dir = end + 1;
else
search_dir = (search_dir + strlen (search_dir));
}
desc_name = 0;
if (!first_backend)
{
DBG_ERR ("Couldn't find any .desc file\n");
return SANE_FALSE;
}
return SANE_TRUE;
}
/* Create a model_record_entry based on a model_entry */
static model_record_entry *
create_model_record (model_entry * model)
{
model_record_entry *model_record;
model_record = calloc (1, sizeof (model_record_entry));
if (!model_record)
{
DBG_ERR ("create_model_record: couldn't calloc model_record_entry\n");
exit (1);
}
model_record->name = model->name;
model_record->status = model->status;
model_record->interface = model->interface;
model_record->url = model->url;
model_record->comment = model->comment;
model_record->usb_vendor_id = model->usb_vendor_id;
model_record->usb_product_id = model->usb_product_id;
model_record->scsi_vendor_id = model->scsi_vendor_id;
model_record->scsi_product_id = model->scsi_product_id;
model_record->scsi_is_processor = model->scsi_is_processor;
return model_record;
}
/* Calculate the priority of statuses: */
/* minimal, basic, good, complete -> 2, untested -> 1, unsupported -> 0 */
static int
calc_priority (status_entry status)
{
switch (status)
{
case status_untested:
return 1;
case status_unsupported:
return 0;
default:
return 2;
}
}
/* Insert model into list at the alphabetically correct position */
static model_record_entry *
update_model_record_list (model_record_entry * first_model_record,
model_entry * model, backend_entry * be)
{
model_record_entry *model_record = first_model_record;
if (!first_model_record)
{
/* First model for this manufacturer */
first_model_record = create_model_record (model);
model_record = first_model_record;
}
else
{
model_record_entry *prev_model_record = 0;
while (model_record)
{
int compare = string_compare (model->name, model_record->name);
if (compare <= 0)
{
model_record_entry *tmp_model_record = model_record;
if ((compare == 0) &&
(string_compare (model->interface, model_record->interface) == 0) &&
(string_compare (model->usb_vendor_id, model_record->usb_vendor_id) == 0) &&
(string_compare (model->usb_product_id, model_record->usb_product_id) == 0))
{
/* Two entries for the same model */
int new_priority = calc_priority (model->status);
int old_priority = calc_priority (model_record->status);
if (new_priority < old_priority)
{
DBG_DBG
("update_model_record_list: model %s ignored, backend %s has "
"higher priority\n", model->name,
model_record->be->name);
return first_model_record;
}
if (new_priority > old_priority)
{
DBG_DBG
("update_model_record_list: model %s overrides the one from backend %s\n",
model->name, model_record->be->name);
tmp_model_record = model_record->next;
}
}
/* correct position */
model_record = create_model_record (model);
model_record->next = tmp_model_record;
if (!prev_model_record)
first_model_record = model_record;
else
prev_model_record->next = model_record;
break;
}
prev_model_record = model_record;
model_record = model_record->next;
}
if (!model_record) /* last entry */
{
prev_model_record->next = create_model_record (model);
model_record = prev_model_record->next;
}
} /* if (first_model_record) */
model_record->be = be;
DBG_DBG ("update_model_record_list: added model %s\n", model->name);
return first_model_record;
}
/* Insert manufacturer into list at the alphabetically correct position, */
/* create new record if neccessary */
static mfg_record_entry *
update_mfg_record_list (mfg_record_entry * first_mfg_record, mfg_entry * mfg,
backend_entry * be)
{
model_entry *model = mfg->model;
mfg_record_entry *mfg_record = first_mfg_record;
while (mfg_record)
{
if (string_compare (mfg_record->name, mfg->name) == 0)
{
/* Manufacturer already exists */
url_entry *mfg_url = mfg->url;
/* Manufacturer comments and (additional) URLs? */
if (!mfg_record->comment)
mfg_record->comment = mfg->comment;
while (mfg_url && mfg_url->name)
{
mfg_record->url = update_url_list (mfg_record->url,
mfg_url->name);
mfg_url = mfg_url->next;
}
break;
}
mfg_record = mfg_record->next;
}
if (!mfg_record)
{
/* Manufacturer doesn't exist yet */
url_entry *url = mfg->url;
mfg_record = calloc (1, sizeof (mfg_record_entry));
if (!mfg_record)
{
DBG_ERR ("update_mfg_record_list: couldn't calloc "
"mfg_record_entry\n");
exit (1);
}
mfg_record->name = mfg->name;
mfg_record->comment = mfg->comment;
while (url)
{
mfg_record->url = update_url_list (mfg_record->url, url->name);
url = url->next;
}
if (first_mfg_record != 0)
{
/* We already have one manufacturer in the list */
mfg_record_entry *new_mfg_record = mfg_record;
mfg_record_entry *prev_mfg_record = 0;
mfg_record = first_mfg_record;
while (mfg_record)
{
int compare =
string_compare (new_mfg_record->name, mfg_record->name);
if (compare <= 0)
{
mfg_record_entry *tmp_mfg_record = mfg_record;
mfg_record = new_mfg_record;
mfg_record->next = tmp_mfg_record;
if (!prev_mfg_record)
first_mfg_record = mfg_record;
else
prev_mfg_record->next = mfg_record;
break;
}
prev_mfg_record = mfg_record;
mfg_record = mfg_record->next;
}
if (!mfg_record) /* last entry */
{
prev_mfg_record->next = new_mfg_record;
mfg_record = prev_mfg_record->next;
}
}
else
first_mfg_record = mfg_record;
DBG_DBG ("update_mfg_record_list: created mfg %s\n", mfg_record->name);
} /* if (!mfg_record) */
/* create model entries */
while (model)
{
mfg_record->model_record =
update_model_record_list (mfg_record->model_record, model, be);
model = model->next;
}
return first_mfg_record;
}
/* Create a sorted list of manufacturers based on the backends list */
static mfg_record_entry *
create_mfg_list (device_type dev_type)
{
mfg_record_entry *first_mfg_record = 0;
backend_entry *be = first_backend;
DBG_DBG ("create_mfg_list: start\n");
while (be)
{
type_entry *type = be->type;
while (type)
{
if (type->type == dev_type)
{
mfg_entry *mfg = type->mfg;
while (mfg)
{
first_mfg_record =
update_mfg_record_list (first_mfg_record, mfg, be);
mfg = mfg->next;
}
}
type = type->next;
}
be = be->next;
}
DBG_DBG ("create_mfg_list: exit\n");
return first_mfg_record;
}
/* Print an ASCII list with all the information we have */
static void
ascii_print_backends (void)
{
backend_entry *be;
be = first_backend;
while (be)
{
url_entry *url = be->url;
type_entry *type = be->type;
if (be->name)
printf ("backend `%s'\n", be->name);
else
printf ("backend *none*\n");
if (be->version)
printf (" version `%s'\n", be->version);
else
printf (" version *none*\n");
if (be->new)
printf (" NEW!\n");
if (be->manpage)
printf (" manpage `%s'\n", be->manpage);
else
printf (" manpage *none*\n");
if (url)
while (url)
{
printf (" url `%s'\n", url->name);
url = url->next;
}
else
printf (" url *none*\n");
if (be->comment)
printf (" comment `%s'\n", be->comment);
else
printf (" comment *none*\n");
if (type)
while (type)
{
switch (type->type)
{
case type_scanner:
printf (" type scanner\n");
break;
case type_stillcam:
printf (" type stillcam\n");
break;
case type_vidcam:
printf (" type vidcam\n");
break;
case type_meta:
printf (" type meta\n");
break;
case type_api:
printf (" type api\n");
break;
default:
printf (" type *unknown*\n");
break;
}
if (type->desc)
{
url_entry *url = type->desc->url;
printf (" desc `%s'\n", type->desc->desc);
if (url)
while (url)
{
printf (" url `%s'\n", url->name);
url = url->next;
}
else
printf (" url *none*\n");
if (type->desc->comment)
printf (" comment `%s'\n", type->desc->comment);
else
printf (" comment *none*\n");
}
else if (type->type >= type_meta)
printf (" desc *none*\n");
if (type->mfg)
{
mfg_entry *mfg = type->mfg;
while (mfg)
{
model_entry *model = mfg->model;
url_entry *url = mfg->url;
printf (" mfg `%s'\n", mfg->name);
if (url)
while (url)
{
printf (" url `%s'\n", url->name);
url = url->next;
}
else
printf (" url *none*\n");
if (mfg->comment)
printf (" comment `%s'\n", mfg->comment);
else
printf (" comment *none*\n");
if (model)
while (model)
{
url_entry *url = model->url;
printf (" model `%s'\n", model->name);
if (model->interface)
printf (" interface `%s'\n", model->interface);
else
printf (" interface *none*\n");
if (model->usb_vendor_id)
printf (" usb-vendor-id `%s'\n", model->usb_vendor_id);
else
printf (" usb-vendor-id *none*\n");
if (model->usb_product_id)
printf (" usb-product-id `%s'\n", model->usb_product_id);
else
printf (" usb-product-id *none*\n");
switch (model->status)
{
case status_minimal:
printf (" status minimal\n");
break;
case status_basic:
printf (" status basic\n");
break;
case status_good:
printf (" status good\n");
break;
case status_complete:
printf (" status complete\n");
break;
case status_untested:
printf (" status untested\n");
break;
case status_unsupported:
printf (" status unsupported\n");
break;
default:
printf (" status *unknown*\n");
break;
}
if (url)
while (url)
{
printf (" url `%s'\n", url->name);
url = url->next;
}
else
printf (" url *none*\n");
if (model->comment)
printf (" comment `%s'\n", model->comment);
else
printf (" comment *none*\n");
model = model->next;
}
else
printf (" model *none*\n");
mfg = mfg->next;
} /* while (mfg) */
}
else if (type->type < type_meta)
printf (" mfg *none*\n");
type = type->next;
} /* while (type) */
else
printf (" type *none*\n");
be = be->next;
} /* while (be) */
}
static char *
clean_string (char *c)
{
/* not avoided characters */
char *aux;
aux = malloc (strlen (c) * sizeof (char) * 6);
*aux = '\0';
while (*c != '\0')
{
switch (*c)
{
case '<':
aux = strcat (aux, "&lt;");
break;
case '>':
aux = strcat (aux, "&gt;");
break;
case '<EFBFBD>':
aux = strcat (aux, "!");
break;
case '<EFBFBD>':
aux = strcat (aux, "?");
break;
case '\'':
aux = strcat (aux, "&apos;");
break;
case '&':
aux = strcat (aux, "&amp;");
break;
default:
aux = strncat (aux, c, 1);
}
c = c + 1;
}
return aux;
}
/* Print an XML list with all the information we have */
static void
xml_print_backends (void)
{
backend_entry *be;
be = first_backend;
printf ("<backends>\n");
while (be)
{
url_entry *url = be->url;
type_entry *type = be->type;
if (be->name)
printf ("<backend name=\"%s\">\n", clean_string (be->name));
else
printf ("<backend name=\"*none\">\n");
if (be->version)
printf ("<version>%s</version> \n", clean_string (be->version));
else
printf ("<version>*none*</version>\n");
if (be->new)
printf ("<new state=\"yes\"/>\n");
else
printf ("<new state=\"no\"/>\n");
if (be->manpage)
printf (" <manpage>%s</manpage>\n", clean_string (be->manpage));
else
printf (" <manpage>*none*</manpage>\n");
if (url)
while (url)
{
printf (" <url>%s</url>\n", clean_string (url->name));
url = url->next;
}
else
printf (" <url>*none*</url>\n");
if (be->comment)
printf (" <comment>%s</comment>\n", clean_string (be->comment));
else
printf (" <comment>*none*</comment>\n");
if (type)
while (type)
{
switch (type->type)
{
case type_scanner:
printf (" <type def=\"scanner\">\n");
break;
case type_stillcam:
printf (" <type def=\"stillcam\">\n");
break;
case type_vidcam:
printf (" <type def=\"vidcam\">\n");
break;
case type_meta:
printf (" <type def=\"meta\">\n");
break;
case type_api:
printf (" <type def=\"api\">\n");
break;
default:
printf (" <type def=\"*unknown*\">\n");
break;
}
if (type->desc)
{
url_entry *url = type->desc->url;
printf (" <desc>%s</desc>\n",
clean_string (type->desc->desc));
if (url)
while (url)
{
printf (" <url>%s</url>\n", clean_string (url->name));
url = url->next;
}
else
printf (" <url>*none*</url>\n");
if (type->desc->comment)
printf (" <comment>%s</comment>\n",
clean_string (type->desc->comment));
else
printf (" <comment>*none*</comment>\n");
}
else if (type->type >= type_meta)
printf (" <desc>*none*</desc>\n");
if (type->mfg)
{
mfg_entry *mfg = type->mfg;
while (mfg)
{
model_entry *model = mfg->model;
url_entry *url = mfg->url;
printf (" <mfg name=\"%s\">\n", clean_string (mfg->name));
if (url)
while (url)
{
printf (" <url>`%s'</url>\n",
clean_string (url->name));
url = url->next;
}
else
printf (" <url>*none*</url>\n");
if (mfg->comment)
printf (" <comment>%s</comment>\n",
clean_string (mfg->comment));
else
printf (" <comment>*none*</comment>\n");
if (model)
while (model)
{
url_entry *url = model->url;
printf (" <model name=\"%s\">\n",
clean_string (model->name));
if (model->interface)
printf (" <interface>%s</interface>\n",
clean_string (model->interface));
else
printf (" <interface>*none*</interface>\n");
if (model->usb_vendor_id)
printf (" <usbvendorid>%s</usbvendorid>\n",
clean_string (model->usb_vendor_id));
else
printf (" <usbvendorid>*none*</usbvendorid>\n");
if (model->usb_product_id)
printf (" <usbproductid>%s</usbproductid>\n",
clean_string (model->usb_product_id));
else
printf (" <usbproductid>*none*</usbproductid>\n");
switch (model->status)
{
case status_minimal:
printf (" <status>minimal</status>\n");
break;
case status_basic:
printf (" <status>basic</status>\n");
break;
case status_good:
printf (" <status>good</status>\n");
break;
case status_complete:
printf (" <status>complete</status>\n");
break;
case status_untested:
printf (" <status>untested</status>\n");
break;
case status_unsupported:
printf (" <status>unsupported</status>\n");
break;
default:
printf (" <status>*unknown*</status>\n");
break;
}
if (url)
while (url)
{
printf (" <url>%s</url>\n",
clean_string (url->name));
url = url->next;
}
else
printf (" <url>*none*</url>\n");
if (model->comment)
printf (" <comment>%s</comment>\n",
clean_string (model->comment));
else
printf (" <comment>*none*</comment>\n");
model = model->next;
printf (" </model>\n");
} /* while (model) */
else
printf (" <model name=\"*none*\" />\n");
printf (" </mfg>\n");
mfg = mfg->next;
} /* while (mfg) */
}
else if (type->type < type_meta)
printf (" <mfg>*none*</mfg>\n");
type = type->next;
printf (" </type>\n");
} /* while (type) */
else
printf (" <type>*none*</type>\n");
printf ("</backend>\n");
be = be->next;
} /* while (be) */
printf ("</backends>\n");
}
/* calculate statistics about supported devices per device type*/
static void
calculate_statistics_per_type (device_type dev_type, statistics_type num)
{
backend_entry *be = first_backend;
while (be)
{
type_entry *type = be->type;
while (type)
{
if (type->type == dev_type)
{
mfg_entry *mfg = type->mfg;
model_entry *model;
if (type->desc)
{
num[status_complete]++;
type = type->next;
continue;
}
if (!mfg)
{
type = type->next;
continue;
}
mfg = type->mfg;
while (mfg)
{
model = mfg->model;
if (model)
{
while (model)
{
enum status_entry status = model->status;
num[status]++;
model = model->next;
} /* while (model) */
} /* if (num_models) */
mfg = mfg->next;
} /* while (mfg) */
} /* if (type->type) */
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
}
static void
html_print_statistics_cell (const char * color, int number)
{
printf ("<td align=center><font color=%s>%d</font></td>\n",
color, number);
}
static void
html_print_statistics_per_type (device_type dev_type)
{
statistics_type num = {0, 0, 0, 0, 0, 0, 0};
status_entry status;
calculate_statistics_per_type (dev_type, num);
printf ("<tr>\n");
printf("<td align=center><a href=\"#%s\">%s</a></td>\n",
device_type_aname [dev_type], device_type_name [dev_type]);
html_print_statistics_cell
(COLOR_UNKNOWN,
num[status_minimal] + num[status_basic] + num[status_good] +
num[status_complete] + num[status_untested] + num[status_untested] +
num[status_unsupported]);
if (dev_type == type_scanner || dev_type == type_stillcam
|| dev_type == type_vidcam)
{
html_print_statistics_cell
(COLOR_UNKNOWN,
num[status_minimal] + num[status_basic] + num[status_good] +
num[status_complete]);
for (status = status_complete; status >= status_unsupported; status--)
html_print_statistics_cell (status_color [status], num [status]);
}
else
{
printf ("<td align=center colspan=7>n/a</td>\n");
}
printf ("</tr>\n");
}
/* print html statistcis */
static void
html_print_summary (void)
{
device_type dev_type;
status_entry status;
printf ("<h2>Summary</h2>\n");
printf ("<table border=1>\n");
printf ("<tr bgcolor=E0E0FF>\n");
printf ("<th align=center rowspan=3>Device type</th>\n");
printf ("<th align=center colspan=8>Number of devices</th>\n");
printf ("</tr>\n");
printf ("<tr bgcolor=E0E0FF>\n");
printf ("<th align=center rowspan=2>Total</th>\n");
printf ("<th align=center colspan=5>Supported</th>\n");
printf ("<th align=center rowspan=2><font color=" COLOR_UNTESTED
">%s</font></th>\n", status_name[status_untested]);
printf ("<th align=center rowspan=2><font color=" COLOR_UNSUPPORTED
">%s</font></th>\n", status_name[status_unsupported]);
printf ("</tr>\n");
printf ("<tr bgcolor=E0E0FF>\n");
printf ("<th align=center>Sum</th>\n");
for (status = status_complete; status >= status_minimal; status--)
printf ("<th align=center><font color=%s>%s</font></th>\n",
status_color[status], status_name[status]);
printf ("</tr>\n");
for (dev_type = type_scanner; dev_type <= type_api; dev_type++)
html_print_statistics_per_type (dev_type);
printf ("</table>\n");
}
/* Generate a name used for <a name=...> HTML tags */
static char *
html_generate_anchor_name (device_type dev_type, char *manufacturer_name)
{
char *name = malloc (strlen (manufacturer_name) + 1 + 2);
char *pointer = name;
char type_char;
if (!name)
{
DBG_ERR ("html_generate_anchor_name: couldn't malloc\n");
return 0;
}
switch (dev_type)
{
case type_scanner:
type_char = 'S';
break;
case type_stillcam:
type_char = 'C';
break;
case type_vidcam:
type_char = 'V';
break;
case type_meta:
type_char = 'M';
break;
case type_api:
type_char = 'A';
break;
default:
type_char = 'Z';
break;
}
snprintf (name, strlen (manufacturer_name) + 1 + 2, "%c-%s",
type_char, manufacturer_name);
while (*pointer)
{
if (!isalnum (*pointer))
*pointer = '-';
else
*pointer = toupper (*pointer);
pointer++;
}
return name;
}
/* Generate one table per backend of all backends providing models */
/* of type dev_type */
static void
html_backends_split_table (device_type dev_type)
{
backend_entry *be = first_backend;
SANE_Bool first = SANE_TRUE;
printf ("<p><b>Backends</b>: \n");
while (be) /* print link list */
{
type_entry *type = be->type;
SANE_Bool found = SANE_FALSE;
while (type)
{
if (type->type == dev_type)
found = SANE_TRUE;
type = type->next;
}
if (found)
{
if (!first)
printf (", \n");
first = SANE_FALSE;
printf ("<a href=\"#%s\">%s</a>",
html_generate_anchor_name (dev_type, be->name), be->name);
}
be = be->next;
}
be = first_backend;
if (first)
printf ("(none)\n");
printf ("</p>\n");
while (be)
{
type_entry *type = be->type;
while (type)
{
if (type->type == dev_type)
{
mfg_entry *mfg = type->mfg;
model_entry *model;
printf ("<h3><a name=\"%s\">Backend: %s\n",
html_generate_anchor_name (type->type, be->name),
be->name);
if (be->version || be->new)
{
printf ("(");
if (be->version)
{
printf ("%s", be->version);
if (be->new)
printf (", <font color=" COLOR_NEW ">NEW!</font>");
}
else
printf ("<font color=" COLOR_NEW ">NEW!</font>");
printf (")\n");
}
printf ("</a></h3>\n");
printf ("<p>\n");
if (be->url && be->url->name)
{
url_entry *url = be->url;
printf ("<b>Link(s):</b> \n");
while (url)
{
if (url != be->url)
printf (", ");
printf ("<a href=\"%s\">%s</a>", url->name, url->name);
url = url->next;
}
printf ("<br>\n");
}
if (be->manpage)
printf ("<b>Manual page:</b> <a href=\"" MAN_PAGE_LINK
"\">%s</a><br>\n", be->manpage, be->manpage);
if (be->comment)
printf ("<b>Comment:</b> %s<br>\n", be->comment);
if (type->desc)
{
if (type->desc->desc)
{
if (type->desc->url && type->desc->url->name)
printf ("<b>Description:</b> "
"<a href=\"%s\">%s</a><br>\n",
type->desc->url->name, type->desc->desc);
else
printf ("<b>Description:</b> %s<br>\n",
type->desc->desc);
}
if (type->desc->comment)
printf ("<b>Comment:</b> %s<br>\n", type->desc->comment);
printf ("</p>\n");
type = type->next;
continue;
}
printf ("</p>\n");
if (!mfg)
{
type = type->next;
continue;
}
printf ("<table border=1>\n");
printf ("<tr bgcolor=E0E0FF>\n");
printf ("<th align=center>Manufacturer</th>\n");
printf ("<th align=center>Model</th>\n");
printf ("<th align=center>Interface</th>\n");
printf ("<th align=center>USB id</th>\n");
printf ("<th align=center>Status</th>\n");
printf ("<th align=center>Comment</th>\n");
printf ("</tr>\n");
mfg = type->mfg;
while (mfg)
{
model = mfg->model;
if (model)
{
int num_models = 0;
while (model) /* count models for rowspan */
{
model = model->next;
num_models++;
}
model = mfg->model;
printf ("<tr>\n");
printf ("<td align=center rowspan=%d>\n", num_models);
if (mfg->url && mfg->url->name)
printf ("<a href=\"%s\">%s</a>\n", mfg->url->name,
mfg->name);
else
printf ("%s\n", mfg->name);
while (model)
{
enum status_entry status = model->status;
if (model != mfg->model)
printf ("<tr>\n");
if (model->url && model->url->name)
printf
("<td align=center><a href=\"%s\">%s</a></td>\n",
model->url->name, model->name);
else
printf ("<td align=center>%s</td>\n",
model->name);
if (model->interface)
printf ("<td align=center>%s</td>\n",
model->interface);
else
printf ("<td align=center>?</td>\n");
if (model->usb_vendor_id && model->usb_product_id)
printf ("<td align=center>%s/%s</td>\n",
model->usb_vendor_id, model->usb_product_id);
else
printf ("<td align=center>&nbsp;</td>\n");
printf ("<td align=center><font color=%s>%s</font></td>\n",
status_color[status], status_name[status]);
if (model->comment && model->comment[0] != 0)
printf ("<td>%s</td>\n", model->comment);
else
printf ("<td>&nbsp;</td>\n");
model = model->next;
printf ("</tr>\n");
} /* while (model) */
} /* if (num_models) */
mfg = mfg->next;
} /* while (mfg) */
printf ("</table>\n");
} /* if (type->type) */
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
/* printf ("</table>\n"); */
}
/* Generate one table per manufacturer constructed of all backends */
/* providing models of type dev_type */
static void
html_mfgs_table (device_type dev_type)
{
mfg_record_entry *mfg_record = 0, *first_mfg_record = 0;
first_mfg_record = create_mfg_list (dev_type);
mfg_record = first_mfg_record;
printf ("<p><b>Manufacturers</b>: \n");
while (mfg_record)
{
if (mfg_record != first_mfg_record)
printf (", \n");
printf ("<a href=\"#%s\">%s</a>",
html_generate_anchor_name (type_unknown, mfg_record->name),
mfg_record->name);
mfg_record = mfg_record->next;
}
mfg_record = first_mfg_record;
if (!mfg_record)
printf ("(none)\n");
printf ("</p>\n");
while (mfg_record)
{
model_record_entry *model_record = mfg_record->model_record;
printf ("<h3><a name=\"%s\">Manufacturer: %s</a></h3>\n",
html_generate_anchor_name (type_unknown, mfg_record->name),
mfg_record->name);
printf ("<p>\n");
if (mfg_record->url && mfg_record->url->name)
{
url_entry *url = mfg_record->url;
printf ("<b>Link(s):</b> \n");
while (url)
{
if (url != mfg_record->url)
printf (", ");
printf ("<a href=\"%s\">%s</a>", url->name, url->name);
url = url->next;
}
printf ("<br>\n");
}
if (mfg_record->comment)
printf ("<b>Comment:</b> %s<br>\n", mfg_record->comment);
printf ("</p>\n");
if (!model_record)
{
mfg_record = mfg_record->next;
continue;
}
printf ("<table border=1>\n");
printf ("<tr bgcolor=E0E0FF>\n");
printf ("<th align=center>Model</th>\n");
printf ("<th align=center>Interface</th>\n");
printf ("<th align=center>USB id</th>\n");
printf ("<th align=center>Status</th>\n");
printf ("<th align=center>Comment</th>\n");
printf ("<th align=center>Backend</th>\n");
printf ("<th align=center>Manpage</th>\n");
printf ("</tr>\n");
while (model_record)
{
enum status_entry status = model_record->status;
if (model_record->url && model_record->url->name)
printf ("<tr><td align=center><a "
"href=\"%s\">%s</a></td>\n",
model_record->url->name, model_record->name);
else
printf ("<tr><td align=center>%s</td>\n", model_record->name);
if (model_record->interface)
printf ("<td align=center>%s</td>\n", model_record->interface);
else
printf ("<td align=center>?</td>\n");
if (model_record->usb_vendor_id && model_record->usb_product_id)
printf ("<td align=center>%s/%s</td>\n",
model_record->usb_vendor_id, model_record->usb_product_id);
else
printf ("<td align=center>&nbsp;</td>\n");
printf ("<td align=center><font color=%s>%s</font></td>\n",
status_color[status], status_name[status]);
if (model_record->comment && model_record->comment[0] != 0)
printf ("<td>%s</td>\n", model_record->comment);
else
printf ("<td>&nbsp;</td>\n");
printf ("<td align=center>\n");
if (model_record->be->url && model_record->be->url->name)
printf ("<a href=\"%s\">%s</a>\n",
model_record->be->url->name, model_record->be->name);
else
printf ("%s", model_record->be->name);
if (model_record->be->version || model_record->be->new)
{
printf ("<br>(");
if (model_record->be->version)
{
printf ("%s", model_record->be->version);
if (model_record->be->new)
printf (", <font color=" COLOR_NEW ">NEW!</font>");
}
else
printf ("<font color=" COLOR_NEW ">NEW!</font>");
printf (")\n");
}
printf ("</td>\n");
if (model_record->be->manpage)
printf ("<td align=center><a href=\""
MAN_PAGE_LINK "\">%s</a></td>\n",
model_record->be->manpage, model_record->be->manpage);
else
printf ("<td align=center>?</td>\n");
printf ("</tr>\n");
model_record = model_record->next;
} /* while model_record */
printf ("</table>\n");
mfg_record = mfg_record->next;
} /* while mfg_record */
}
/* Print the HTML headers and an introduction */
static void
html_print_header (void)
{
printf
("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
"<html> <head>\n"
"<meta http-equiv=\"Content-Type\" content=\"text/html; "
"charset=iso-8859-1\">\n");
printf ("<title>%s</title>\n", title);
printf
("</head>\n"
"<body bgcolor=FFFFFF>\n"
"<div align=center>\n"
"<img src=\"http://www.sane-project.org/images/sane.png\" alt=\"SANE\">\n");
printf ("<h1>%s</h1>\n", title);
printf ("</div>\n" "<hr>\n");
printf ("%s\n", intro);
printf
("<p>This is only a summary!\n"
"Please consult the manpages and the author-supplied webpages\n"
"for more detailed (and usually important) information\n"
"concerning each backend.</p>\n");
printf
("<p>If you have new information or corrections, please file a\n"
"<a href=\"http://www.sane-project.org/bugs.html\">bug report</a>\n"
"with as many details as possible. Also please tell us if your scanner \n"
"isn't mentioned in this list at all.</p>\n"
"<p>For an explanation of the tables, see the\n"
"<a href=\"#legend\">legend</a>.\n");
}
/* Print the HTML footers and contact information */
static void
html_print_footer (void)
{
time_t current_time = time (0);
printf
("<hr>\n"
"<a href=\"http://www.sane-project.org/\">SANE homepage</a>\n"
"<address>\n"
"<a href=\"http://www.sane-project.org/imprint.html\"\n"
">Contact</a>\n" "</address>\n" "<font size=-1>\n");
printf ("This page was last updated on %s by sane-desc %s from %s\n",
asctime (localtime (&current_time)), SANE_DESC_VERSION, PACKAGE_STRING);
printf ("</font>\n");
printf ("</body> </html>\n");
}
/* print parts of the legend */
static void
html_print_legend_backend (void)
{
printf
(" <dt><b>Backend:</b></dt>\n"
" <dd>Name of the backend, in parentheses if available:\n"
" Version of backend/driver; newer versions may be\n"
" available from their home sites.<br>"
" <font color=" COLOR_NEW ">NEW!</font> means brand-new to the\n"
" current release of SANE.<br>\n"
" UNMAINTAINED means that nobody maintains that backend. Expect no \n"
" new features or newly supported devices. You are welcome to take over \n"
" maintainership.\n" " </dd>\n");
}
static void
html_print_legend_link (void)
{
printf
(" <dt><b>Link(s):</b></dt>\n"
" <dd>Link(s) to more extensive and\n"
" detailed information, if it exists, or the email address\n"
" of the author or maintainer.\n");
}
static void
html_print_legend_manual (void)
{
printf
(" <dt><b>Manual Page:</b></dt>\n"
" <dd>A link to the man-page online, if it exists.</dd>\n");
}
static void
html_print_legend_comment (void)
{
printf
(" <dt><b>Comment:</b></dt>\n"
" <dd>More information about the backend or model, e.g. the level of "
" support and possible problems.</dd>\n");
}
static void
html_print_legend_manufacturer (void)
{
printf
(" <dt><b>Manufacturer:</b></dt>\n"
" <dd>Manufacturer, vendor or brand name of the device.</dd>\n");
}
static void
html_print_legend_model (void)
{
printf
(" <dt><b>Model:</b></dt>\n" " <dd>Name of the the device.</dd>\n");
}
static void
html_print_legend_interface (void)
{
printf
(" <dt><b>Interface:</b></dt>\n"
" <dd>How the device is connected to the computer.</dd>\n");
}
static void
html_print_legend_usbid (void)
{
printf
(" <dt><b>USB id:</b></dt>\n"
" <dd>The USB vendor and product ids as printed by sane-find-scanner -q (only applicable for USB devices).</dd>\n");
}
static void
html_print_legend_status (void)
{
printf
(" <dt><b>Status</b>:</dt>\n"
" <dd>Indicates how many of the features the device provides \n"
" are supported by SANE.\n"
" <ul><li><font color=" COLOR_UNSUPPORTED ">unsupported</font>"
" means the device is not supported at least by this backend. "
" It may be supported by other backends, however.\n");
printf
(" <li><font color=" COLOR_UNTESTED ">untested</font> means the "
" device may be supported but couldn't be tested. Be very "
" careful and report success/failure.\n"
" <li><font color=" COLOR_MINIMAL ">minimal</font> means that the\n"
" device is detected and scans at least in one mode. But the quality \n"
" is bad or important features won't work.\n");
printf
(" <li><font color=" COLOR_BASIC ">basic</font> means it works at \n"
" least in the most important modes but quality is not perfect.\n"
" <li><font color=" COLOR_GOOD
">good</font> means the device is usable \n"
" for day-to-day work. Some rather exotic features may be missing.\n"
" <li><font color=" COLOR_COMPLETE
">complete</font> means the backends \n"
" supports everything the device can do.\n" " </ul></dd>\n");
}
static void
html_print_legend_description (void)
{
printf
(" <dt><b>Description</b>:</dt>\n"
" <dd>The scope of application of the backend.\n");
}
/* Print the HTML page with one table of models per backend */
static void
html_print_backends_split (void)
{
if (!title)
title = "SANE: Backends (Drivers)";
if (!intro)
intro = "<p> The following table summarizes the backends/drivers "
"distributed with the latest version of sane-backends, and the hardware "
"or software they support. </p>";
html_print_header ();
html_print_summary ();
printf ("<h2><a name=\"SCANNERS\">Scanners</a></h2>\n");
html_backends_split_table (type_scanner);
printf ("<h2><a name=\"STILL\">Still Cameras</a></h2>\n");
html_backends_split_table (type_stillcam);
printf ("<h2><a name=\"VIDEO\">Video Cameras</a></h2>\n");
html_backends_split_table (type_vidcam);
printf ("<h2><a name=\"API\">APIs</a></h2>\n");
html_backends_split_table (type_api);
printf ("<h2><a name=\"META\">Meta Backends</a></h2>\n");
html_backends_split_table (type_meta);
printf ("<h3><a name=\"legend\">Legend:</a></h3>\n" "<blockquote><dl>\n");
html_print_legend_backend ();
html_print_legend_link ();
html_print_legend_manual ();
html_print_legend_comment ();
html_print_legend_manufacturer ();
html_print_legend_model ();
html_print_legend_interface ();
html_print_legend_usbid ();
html_print_legend_status ();
html_print_legend_description ();
printf ("</dl></blockquote>\n");
html_print_footer ();
}
/* Print the HTML page with one table of models per manufacturer */
static void
html_print_mfgs (void)
{
if (!title)
title = "SANE: Supported Devices";
if (!intro)
intro = "<p> The following table summarizes the devices supported "
"by the latest version of sane-backends. </p>";
html_print_header ();
html_print_summary ();
printf ("<h2><a name=\"SCANNERS\">Scanners</a></h2>\n");
html_mfgs_table (type_scanner);
printf ("<h2><a name=\"STILL\">Still Cameras</a></h2>\n");
html_mfgs_table (type_stillcam);
printf ("<h2><a name=\"VIDEO\">Video Cameras</a></h2>\n");
html_mfgs_table (type_vidcam);
printf ("<h2><a name=\"API\">APIs</a></h2>\n");
html_backends_split_table (type_api);
printf ("<h2><a name=\"META\">Meta Backends</a></h2>\n");
html_backends_split_table (type_meta);
printf
("<h3><a name=\"legend\">Legend:</a></h3>\n" "<blockquote>\n" "<dl>\n");
html_print_legend_model ();
html_print_legend_interface ();
html_print_legend_usbid ();
html_print_legend_status ();
html_print_legend_comment ();
html_print_legend_backend ();
html_print_legend_manual ();
html_print_legend_manufacturer ();
html_print_legend_description ();
printf ("</dl>\n" "</blockquote>\n");
html_print_footer ();
}
/* print statistics about supported devices */
static void
print_statistics_per_type (device_type dev_type)
{
statistics_type num = {0, 0, 0, 0, 0, 0, 0};
calculate_statistics_per_type (dev_type, num);
printf (" Total: %4d\n",
num[status_minimal] + num[status_basic] + num[status_good] +
num[status_complete] + num[status_untested] + num[status_untested] +
num[status_unsupported]);
if (dev_type == type_scanner || dev_type == type_stillcam
|| dev_type == type_vidcam)
{
printf (" Supported: %4d (complete: %d, good: %d, basic: %d, "
"minimal: %d)\n",
num[status_minimal] + num[status_basic] + num[status_good] +
num[status_complete], num[status_complete], num[status_good],
num[status_basic], num[status_minimal]);
printf (" Untested: %4d\n", num[status_untested]);
printf (" Unsupported: %4d\n", num[status_unsupported]);
}
}
static void
print_statistics (void)
{
printf ("Number of known devices:\n");
printf ("Scanners:\n");
print_statistics_per_type (type_scanner);
printf ("Still cameras:\n");
print_statistics_per_type (type_stillcam);
printf ("Video cameras:\n");
print_statistics_per_type (type_vidcam);
printf ("Meta backends:\n");
print_statistics_per_type (type_meta);
printf ("API backends:\n");
print_statistics_per_type (type_api);
}
static usbid_type *
create_usbid (char *manufacturer, char *model,
char *usb_vendor_id, char *usb_product_id)
{
usbid_type * usbid = calloc (1, sizeof (usbid_type));
usbid->usb_vendor_id = strdup (usb_vendor_id);
usbid->usb_product_id = strdup (usb_product_id);
usbid->name = calloc (1, sizeof (manufacturer_model_type));
usbid->name->name = calloc (1, strlen (manufacturer) + strlen (model) + 3);
sprintf (usbid->name->name, "%s %s", manufacturer, model);
usbid->name->next = 0;
usbid->next = 0;
DBG_DBG ("New USB ids: %s/%s (%s %s)\n", usb_vendor_id, usb_product_id,
manufacturer, model);
return usbid;
}
static scsiid_type *
create_scsiid (char *manufacturer, char *model,
char *scsi_vendor_id, char *scsi_product_id, SANE_Bool is_processor)
{
scsiid_type * scsiid = calloc (1, sizeof (scsiid_type));
scsiid->scsi_vendor_id = strdup (scsi_vendor_id);
scsiid->scsi_product_id = strdup (scsi_product_id);
scsiid->is_processor = is_processor;
scsiid->name = calloc (1, sizeof (manufacturer_model_type));
scsiid->name->name = calloc (1, strlen (manufacturer) + strlen (model) + 3);
sprintf (scsiid->name->name, "%s %s", manufacturer, model);
scsiid->name->next = 0;
scsiid->next = 0;
DBG_DBG ("New SCSI ids: %s/%s (%s %s)\n", scsi_vendor_id, scsi_product_id,
manufacturer, model);
return scsiid;
}
static usbid_type *
add_usbid (usbid_type *first_usbid, char *manufacturer, char *model,
char *usb_vendor_id, char *usb_product_id)
{
usbid_type *usbid = first_usbid;
usbid_type *prev_usbid = 0, *tmp_usbid = 0;
if (!first_usbid)
first_usbid = create_usbid (manufacturer, model, usb_vendor_id, usb_product_id);
else
{
while (usbid)
{
if (strcmp (usb_vendor_id, usbid->usb_vendor_id) == 0 &&
strcmp (usb_product_id, usbid->usb_product_id) == 0)
{
manufacturer_model_type *man_mod = usbid->name;
while (man_mod->next)
man_mod = man_mod->next;
man_mod->next = malloc (sizeof (manufacturer_model_type));
man_mod->next->name = malloc (strlen (manufacturer) + strlen (model) + 3);
sprintf (man_mod->next->name, "%s %s", manufacturer, model);
man_mod->next->next = 0;
DBG_DBG ("Added manufacturer/model %s %s to USB ids %s/%s\n", manufacturer, model,
usb_vendor_id, usb_product_id);
break;
}
if (strcmp (usb_vendor_id, usbid->usb_vendor_id) < 0 ||
(strcmp (usb_vendor_id, usbid->usb_vendor_id) == 0 &&
strcmp (usb_product_id, usbid->usb_product_id) < 0))
{
tmp_usbid = create_usbid (manufacturer, model, usb_vendor_id, usb_product_id);
tmp_usbid->next = usbid;
if (prev_usbid)
prev_usbid->next = tmp_usbid;
else
first_usbid = tmp_usbid;
break;
}
prev_usbid = usbid;
usbid = usbid->next;
}
if (!usbid)
{
prev_usbid->next = create_usbid (manufacturer, model, usb_vendor_id, usb_product_id);
usbid = prev_usbid->next;
}
}
return first_usbid;
}
static scsiid_type *
add_scsiid (scsiid_type *first_scsiid, char *manufacturer, char *model,
char *scsi_vendor_id, char *scsi_product_id, SANE_Bool is_processor)
{
scsiid_type *scsiid = first_scsiid;
scsiid_type *prev_scsiid = 0, *tmp_scsiid = 0;
if (!first_scsiid)
first_scsiid = create_scsiid (manufacturer, model, scsi_vendor_id, scsi_product_id, is_processor);
else
{
while (scsiid)
{
if (strcmp (scsi_vendor_id, scsiid->scsi_vendor_id) == 0 &&
strcmp (scsi_product_id, scsiid->scsi_product_id) == 0)
{
manufacturer_model_type *man_mod = scsiid->name;
while (man_mod->next)
man_mod = man_mod->next;
man_mod->next = malloc (sizeof (manufacturer_model_type));
man_mod->next->name = malloc (strlen (manufacturer) + strlen (model) + 3);
sprintf (man_mod->next->name, "%s %s", manufacturer, model);
man_mod->next->next = 0;
DBG_DBG ("Added manufacturer/model %s %s to SCSI ids %s/%s\n", manufacturer, model,
scsi_vendor_id, scsi_product_id);
break;
}
if (strcmp (scsi_vendor_id, scsiid->scsi_vendor_id) < 0 ||
(strcmp (scsi_vendor_id, scsiid->scsi_vendor_id) == 0 &&
strcmp (scsi_product_id, scsiid->scsi_product_id) < 0))
{
tmp_scsiid = create_scsiid (manufacturer, model, scsi_vendor_id, scsi_product_id, is_processor);
tmp_scsiid->next = scsiid;
if (prev_scsiid)
prev_scsiid->next = tmp_scsiid;
else
first_scsiid = tmp_scsiid;
break;
}
prev_scsiid = scsiid;
scsiid = scsiid->next;
}
if (!scsiid)
{
prev_scsiid->next = create_scsiid (manufacturer, model, scsi_vendor_id, scsi_product_id, is_processor);
scsiid = prev_scsiid->next;
}
}
return first_scsiid;
}
static usbid_type *
create_usbids_table (void)
{
backend_entry *be;
usbid_type *first_usbid = NULL;
if (!first_backend)
return NULL;
for (be = first_backend; be; be = be->next)
{
type_entry *type;
if (!be->type)
continue;
for (type = be->type; type; type = type->next)
{
mfg_entry *mfg;
if (!type->mfg)
continue;
for (mfg = type->mfg; mfg; mfg = mfg->next)
{
model_entry *model;
if (!mfg->model)
continue;
for (model = mfg->model; model; model = model->next)
{
if ((model->status == status_unsupported)
|| (model->status == status_unknown))
continue;
if (model->usb_vendor_id && model->usb_product_id)
{
first_usbid = add_usbid (first_usbid, mfg->name,
model->name,
model->usb_vendor_id,
model->usb_product_id);
}
} /* for (model) */
} /* for (mfg) */
} /* for (type) */
} /* for (be) */
return first_usbid;
}
static scsiid_type *
create_scsiids_table (void)
{
backend_entry *be;
scsiid_type *first_scsiid = NULL;
if (!first_backend)
return NULL;
for (be = first_backend; be; be = be->next)
{
type_entry *type;
if (!be->type)
continue;
for (type = be->type; type; type = type->next)
{
mfg_entry *mfg;
if (!type->mfg)
continue;
for (mfg = type->mfg; mfg; mfg = mfg->next)
{
model_entry *model;
if (!mfg->model)
continue;
for (model = mfg->model; model; model = model->next)
{
if ((model->status == status_unsupported)
|| (model->status == status_unknown))
continue;
if (model->scsi_vendor_id && model->scsi_product_id)
{
first_scsiid = add_scsiid (first_scsiid, mfg->name,
model->name,
model->scsi_vendor_id,
model->scsi_product_id,
model->scsi_is_processor);
}
} /* for (model) */
} /* for (mfg) */
} /* for (type) */
} /* for (be) */
return first_scsiid;
}
/* print USB usermap file to be used by the hotplug tools */
static void
print_usermap_header (void)
{
time_t current_time = time (0);
printf ("# This file was automatically created based on description files (*.desc)\n"
"# by sane-desc %s from %s on %s",
SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (&current_time)));
printf
("#\n"
"# The entries below are used to detect a USB device when it's plugged in\n"
"# and then run the libusbscanner script to change the ownership and\n"
"# permissions on the \"device node\" used by libusb.\n"
"#\n"
"# The 0x0003 match flag means the device is matched by its vendor and\n"
"# product IDs.\n"
"#\n"
"# Sample entry (replace 0xVVVV and 0xPPPP with vendor ID and product ID\n"
"# respectively):\n");
printf
("# libusbscanner 0x0003 0xVVVV 0xPPPP 0x0000 0x0000 0x00 0x00 0x00 0x00 "
"0x00 0x00 0x00000000\n"
"# usb module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi "
"bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass "
"bInterfaceSubClass bInterfaceProtocol driver_info\n"
"#\n"
"# The following list already contains a lot of scanners. If your scanner\n"
"# isn't mentioned there, add it as explained above and mail the entry to\n"
"# the sane-devel mailing list (sane-devel@lists.alioth.debian.org).\n"
"#\n");
}
static void
print_usermap (void)
{
usbid_type *usbid = create_usbids_table ();
print_usermap_header ();
while (usbid)
{
manufacturer_model_type * name = usbid->name;
printf ("# ");
while (name)
{
if (name != usbid->name)
printf (" | ");
printf ("%s", name->name);
name = name->next;
}
printf ("\n");
printf ("libusbscanner 0x0003 %s %s ", usbid->usb_vendor_id,
usbid->usb_product_id);
printf ("0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000\n");
usbid = usbid->next;
}
}
/* print libsane.db file for hotplug-ng */
static void
print_db_header (void)
{
time_t current_time = time (0);
printf ("# This file was automatically created based on description files (*.desc)\n"
"# by sane-desc %s from %s on %s",
SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (&current_time)));
printf
("#\n"
"# The entries below are used to detect a USB device when it's plugged in\n"
"# and then run a script to change the ownership and\n"
"# permissions on the \"device node\" used by libusb.\n"
"# Sample entry (replace 0xVVVV and 0xPPPP with vendor ID and product ID\n"
"# respectively):\n");
printf
("#\n"
"# 0xVVVV<tab>0xPPPP<tab>%s:%s<tab>%s<tab>[/usr/local/bin/foo.sh]\n"
"# Fields:\n"
"# vendor ID\n"
"# product ID\n"
"# ownership (user:group)\n"
"# permissions\n"
"# path of an optional script to run (it can be omitted)\n", DEVOWNER, DEVGROUP, DEVMODE);
printf
("#\n"
"# The following list already contains a lot of scanners. If your scanner\n"
"# isn't mentioned there, add it as explained above and mail the entry to\n"
"# the sane-devel mailing list (sane-devel@lists.alioth.debian.org).\n"
"#\n");
}
static void
print_db (void)
{
usbid_type *usbid = create_usbids_table ();
print_db_header ();
while (usbid)
{
manufacturer_model_type * name = usbid->name;
printf ("# ");
while (name)
{
if (name != usbid->name)
printf (" | ");
printf ("%s", name->name);
name = name->next;
}
printf ("\n");
printf ("%s\t%s\t%s:%s\t%s\t\n", usbid->usb_vendor_id,
usbid->usb_product_id, DEVOWNER, DEVGROUP, DEVMODE);
usbid = usbid->next;
}
}
/* print libsane.rules for Linux udev */
static void
print_udev_header (void)
{
time_t current_time = time (0);
printf ("# This file was automatically created based on description files (*.desc)\n"
"# by sane-desc %s from %s on %s",
SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (&current_time)));
printf
("#\n"
"# udev rules file for supported USB and SCSI devices\n"
"#\n"
"# The SCSI device support is very basic and includes only\n"
"# scanners that mark themselves as type \"scanner\" or\n"
"# SCSI-scanners from HP and other vendors that are entitled \"processor\"\n"
"# but are treated accordingly.\n"
"#\n");
printf
("# To add a USB device, add a rule to the list below between the\n"
"# LABEL=\"libsane_usb_rules_begin\" and LABEL=\"libsane_usb_rules_end\" lines.\n"
"#\n"
"# To run a script when your device is plugged in, add RUN+=\"/path/to/script\"\n"
"# to the appropriate rule.\n");
printf
("#\n"
"# The following list already contains a lot of scanners. If your scanner\n"
"# isn't mentioned there, add it as explained above and mail the entry to\n"
"# the sane-devel mailing list (sane-devel@lists.alioth.debian.org).\n"
"#\n\n");
}
static void
print_udev (void)
{
usbid_type *usbid = create_usbids_table ();
scsiid_type *scsiid = create_scsiids_table ();
int i;
print_udev_header ();
printf("ACTION!=\"add\", GOTO=\"libsane_rules_end\"\n"
"ENV{DEVTYPE}==\"usb_device\", GOTO=\"libsane_create_usb_dev\"\n"
"SUBSYSTEMS==\"scsi\", GOTO=\"libsane_scsi_rules_begin\"\n"
"SUBSYSTEM==\"usb_device\", GOTO=\"libsane_usb_rules_begin\"\n"
"SUBSYSTEM!=\"usb_device\", GOTO=\"libsane_usb_rules_end\"\n"
"\n");
printf("# Kernel >= 2.6.22 jumps here\n"
"LABEL=\"libsane_create_usb_dev\"\n"
"\n");
printf("# For Linux >= 2.6.22 without CONFIG_USB_DEVICE_CLASS=y\n"
"# If the following rule does not exist on your system yet, uncomment it\n"
"# ENV{DEVTYPE}==\"usb_device\", NAME=\"bus/usb/$env{BUSNUM}/$env{DEVNUM}\", "
"MODE=\"0664\", OWNER=\"root\", GROUP=\"root\"\n"
"\n");
printf("# Kernel < 2.6.22 jumps here\n"
"LABEL=\"libsane_usb_rules_begin\"\n"
"\n");
while (usbid)
{
manufacturer_model_type * name = usbid->name;
i = 0;
printf ("# ");
while (name)
{
if ((name != usbid->name) && (i > 0))
printf (" | ");
printf ("%s", name->name);
name = name->next;
i++;
/*
* Limit the number of model names on the same line to 3,
* as udev cannot handle very long lines and prints a warning
* message while loading the rules files.
*/
if ((i == 3) && (name != NULL))
{
printf("\n# ");
i = 0;
}
}
printf ("\n");
printf ("ATTR{idVendor}==\"%s\", ATTR{idProduct}==\"%s\", MODE=\"%s\", GROUP=\"%s\", ENV{libsane_matched}=\"yes\"\n",
usbid->usb_vendor_id + 2, usbid->usb_product_id + 2, DEVMODE, DEVGROUP);
usbid = usbid->next;
}
printf("\n# The following rule will disable USB autosuspend for the device\n");
printf("ENV{libsane_matched}==\"yes\", RUN+=\"/bin/sh -c 'test -e /sys/$env{DEVPATH}/power/level && echo on > /sys/$env{DEVPATH}/power/level'\"\n");
printf ("\nLABEL=\"libsane_usb_rules_end\"\n\n");
printf ("SUBSYSTEMS!=\"scsi\", GOTO=\"libsane_scsi_rules_end\"\n\n");
printf ("LABEL=\"libsane_scsi_rules_begin\"\n");
printf ("# Generic: SCSI device type 6 indicates a scanner\n");
printf ("KERNEL==\"sg[0-9]*\", NAME=\"%%k\", ATTRS{type}==\"6\", MODE=\"%s\", GROUP=\"%s\"\n", DEVMODE, DEVGROUP);
printf ("# Some scanners advertise themselves as SCSI device type 3\n");
while (scsiid)
{
manufacturer_model_type * name = scsiid->name;
if (!scsiid->is_processor)
continue;
i = 0;
printf ("# ");
while (name)
{
if ((name != scsiid->name) && (i > 0))
printf (" | ");
printf ("%s", name->name);
name = name->next;
i++;
/*
* Limit the number of model names on the same line to 3,
* as udev cannot handle very long lines and prints a warning
* message while loading the rules files.
*/
if ((i == 3) && (name != NULL))
{
printf("\n# ");
i = 0;
}
}
printf ("\n");
printf ("KERNEL==\"sg[0-9]*\", NAME=\"%%k\", ATTRS{type}==\"3\", ATTRS{vendor}==\"%s\", ATTRS{model}==\"%s\", MODE=\"%s\", GROUP=\"%s\"\n",
scsiid->scsi_vendor_id, scsiid->scsi_product_id, DEVMODE, DEVGROUP);
scsiid = scsiid->next;
}
printf ("LABEL=\"libsane_scsi_rules_end\"\n\n");
printf ("LABEL=\"libsane_rules_end\"\n");
}
static void
print_plist (void)
{
usbid_type *usbid = create_usbids_table ();
printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
printf ("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
printf ("<plist version=\"1.0\">\n");
printf ("<dict>\n");
printf ("\t<key>device info version</key>\n");
printf ("\t<string>2.0</string>\n");
printf ("\t<key>usb</key>\n");
printf ("\t<dict>\n");
printf ("\t\t<key>IOUSBDevice</key>\n");
printf ("\t\t<array>\n");
while (usbid)
{
printf ("\t\t\t<dict>\n");
printf ("\t\t\t\t<key>device type</key>\n");
printf ("\t\t\t\t<string>scanner</string>\n");
printf ("\t\t\t\t<key>product</key>\n");
printf ("\t\t\t\t<string>%s</string>\n", usbid->usb_product_id);
printf ("\t\t\t\t<key>vendor</key>\n");
printf ("\t\t\t\t<string>%s</string>\n", usbid->usb_vendor_id);
printf ("\t\t\t</dict>\n");
usbid = usbid->next;
}
printf ("\t\t</array>\n");
printf ("\t</dict>\n");
printf ("</dict>\n");
printf ("</plist>\n");
}
static void
print_hal (int new)
{
int i;
SANE_Bool in_match;
char *last_vendor;
scsiid_type *scsiid = create_scsiids_table ();
usbid_type *usbid = create_usbids_table ();
printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
printf ("<deviceinfo version=\"0.2\">\n");
printf (" <device>\n");
printf (" <!-- SCSI-SUBSYSTEM -->\n");
printf (" <match key=\"info.category\" string=\"scsi_generic\">\n");
printf (" <!-- Some SCSI Scanners announce themselves \"processor\" -->\n");
printf (" <match key=\"@info.parent:scsi.type\" string=\"processor\">\n");
last_vendor = "";
in_match = SANE_FALSE;
while (scsiid)
{
manufacturer_model_type * name = scsiid->name;
if (!scsiid->is_processor)
{
scsiid = scsiid->next;
continue;
}
if (strcmp(last_vendor, scsiid->scsi_vendor_id) != 0)
{
if (in_match)
printf (" </match>\n");
printf (" <match key=\"@info.parent:scsi.vendor\" string=\"%s\">\n", scsiid->scsi_vendor_id);
last_vendor = scsiid->scsi_vendor_id;
in_match = SANE_TRUE;
}
printf (" <!-- SCSI Scanner ");
while (name)
{
if (name != scsiid->name)
printf (" | ");
printf ("\"%s\"", name->name);
name = name->next;
}
printf (" -->\n");
printf (" <match key=\"@info.parent:scsi.model\" string=\"%s\">\n", scsiid->scsi_product_id);
printf (" <append key=\"info.capabilities\" type=\"strlist\">scanner</append>\n");
printf (" </match>\n");
scsiid = scsiid->next;
}
if (in_match)
printf (" </match>\n");
printf (" </match>\n");
printf (" </match>\n");
printf (" <!-- USB-SUBSYSTEM -->\n");
if (new)
printf (" <match key=\"info.subsystem\" string=\"usb\">\n");
else
printf (" <match key=\"info.bus\" string=\"usb\">\n");
last_vendor = "";
in_match = SANE_FALSE;
while (usbid)
{
manufacturer_model_type * name = usbid->name;
if (strcmp(last_vendor, usbid->usb_vendor_id) != 0)
{
if (in_match)
printf (" </match>\n");
printf (" <match key=\"usb.vendor_id\" int=\"%s\">\n", usbid->usb_vendor_id);
last_vendor = usbid->usb_vendor_id;
in_match = SANE_TRUE;
}
i = 0;
printf (" <!-- ");
while (name)
{
if ((name != usbid->name) && (i > 0))
printf (" | ");
printf ("%s", name->name);
name = name->next;
i++;
if ((i == 3) && (name != NULL))
{
printf("\n ");
i = 0;
}
}
printf (" -->\n");
printf (" <match key=\"usb.product_id\" int=\"%s\">\n", usbid->usb_product_id);
printf (" <append key=\"info.capabilities\" type=\"strlist\">scanner</append>\n");
printf (" <merge key=\"scanner.access_method\" type=\"string\">proprietary</merge>\n");
printf (" </match>\n");
usbid = usbid->next;
}
if (in_match)
printf (" </match>\n");
printf (" </match>\n");
printf (" </device>\n");
printf ("</deviceinfo>\n");
}
int
main (int argc, char **argv)
{
program_name = strrchr (argv[0], '/');
if (program_name)
++program_name;
else
program_name = argv[0];
if (!get_options (argc, argv))
return 1;
if (!read_files ())
return 1;
switch (mode)
{
case output_mode_ascii:
ascii_print_backends ();
break;
case output_mode_xml:
xml_print_backends ();
break;
case output_mode_html_backends_split:
html_print_backends_split ();
break;
case output_mode_html_mfgs:
html_print_mfgs ();
break;
case output_mode_statistics:
print_statistics ();
break;
case output_mode_usermap:
print_usermap ();
break;
case output_mode_db:
print_db ();
break;
case output_mode_udev:
print_udev ();
break;
case output_mode_plist:
print_plist ();
break;
case output_mode_hal:
print_hal (0);
break;
case output_mode_halnew:
print_hal (1);
break;
default:
DBG_ERR ("Unknown output mode\n");
return 1;
}
return 0;
}