/*
sane-desc.c -- generate list of supported SANE devices
Copyright (C) 2002-2006 Henning Meier-Geinitz
Copyright (C) 2004 Jose Gato (XML output)
Copyright (C) 2006 Mattias Ellert (plist output)
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.
*/
#define SANE_DESC_VERSION "3.3"
#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\""
#include <../include/sane/config.h>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/sanei_config.h"
#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
}
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;
}
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;
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;
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 \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 "
"\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;
}
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;
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, ":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;
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, "<");
break;
case '>':
aux = strcat (aux, ">");
break;
case '¡':
aux = strcat (aux, "!");
break;
case '¿':
aux = strcat (aux, "?");
break;
case '\'':
aux = strcat (aux, "'");
break;
case '&':
aux = strcat (aux, "&");
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 ("\n");
while (be)
{
url_entry *url = be->url;
type_entry *type = be->type;
if (be->name)
printf ("\n", clean_string (be->name));
else
printf ("\n");
if (be->version)
printf ("%s \n", clean_string (be->version));
else
printf ("*none*\n");
if (be->new)
printf ("\n");
else
printf ("\n");
if (be->manpage)
printf (" %s\n", clean_string (be->manpage));
else
printf (" *none*\n");
if (url)
while (url)
{
printf (" %s\n", clean_string (url->name));
url = url->next;
}
else
printf (" *none*\n");
if (be->comment)
printf (" %s\n", clean_string (be->comment));
else
printf (" *none*\n");
if (type)
while (type)
{
switch (type->type)
{
case type_scanner:
printf (" \n");
break;
case type_stillcam:
printf (" \n");
break;
case type_vidcam:
printf (" \n");
break;
case type_meta:
printf (" \n");
break;
case type_api:
printf (" \n");
break;
default:
printf (" \n");
break;
}
if (type->desc)
{
url_entry *url = type->desc->url;
printf (" %s\n",
clean_string (type->desc->desc));
if (url)
while (url)
{
printf (" %s\n", clean_string (url->name));
url = url->next;
}
else
printf (" *none*\n");
if (type->desc->comment)
printf (" %s\n",
clean_string (type->desc->comment));
else
printf (" *none*\n");
}
else if (type->type >= type_meta)
printf (" *none*\n");
if (type->mfg)
{
mfg_entry *mfg = type->mfg;
while (mfg)
{
model_entry *model = mfg->model;
url_entry *url = mfg->url;
printf (" \n", clean_string (mfg->name));
if (url)
while (url)
{
printf (" `%s'\n",
clean_string (url->name));
url = url->next;
}
else
printf (" *none*\n");
if (mfg->comment)
printf (" %s\n",
clean_string (mfg->comment));
else
printf (" *none*\n");
if (model)
while (model)
{
url_entry *url = model->url;
printf (" \n",
clean_string (model->name));
if (model->interface)
printf (" %s\n",
clean_string (model->interface));
else
printf (" *none*\n");
if (model->usb_vendor_id)
printf (" %s\n",
clean_string (model->usb_vendor_id));
else
printf (" *none*\n");
if (model->usb_product_id)
printf (" %s\n",
clean_string (model->usb_product_id));
else
printf (" *none*\n");
switch (model->status)
{
case status_minimal:
printf (" minimal\n");
break;
case status_basic:
printf (" basic\n");
break;
case status_good:
printf (" good\n");
break;
case status_complete:
printf (" complete\n");
break;
case status_untested:
printf (" untested\n");
break;
case status_unsupported:
printf (" unsupported\n");
break;
default:
printf (" *unknown*\n");
break;
}
if (url)
while (url)
{
printf (" %s\n",
clean_string (url->name));
url = url->next;
}
else
printf (" *none*\n");
if (model->comment)
printf (" %s\n",
clean_string (model->comment));
else
printf (" *none*\n");
model = model->next;
printf (" \n");
} /* while (model) */
else
printf (" \n");
printf (" \n");
mfg = mfg->next;
} /* while (mfg) */
}
else if (type->type < type_meta)
printf (" *none*\n");
type = type->next;
printf (" \n");
} /* while (type) */
else
printf (" *none*\n");
printf ("\n");
be = be->next;
} /* while (be) */
printf ("\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 ("%d | \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 ("\n");
printf("%s | \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 ("n/a | \n");
}
printf ("
\n");
}
/* print html statistcis */
static void
html_print_summary (void)
{
device_type dev_type;
status_entry status;
printf ("Summary
\n");
printf ("\n");
printf ("\n");
printf ("Device type | \n");
printf ("Number of devices | \n");
printf ("
\n");
printf ("\n");
printf ("Total | \n");
printf ("Supported | \n");
printf ("%s | \n", status_name[status_untested]);
printf ("%s | \n", status_name[status_unsupported]);
printf ("
\n");
printf ("\n");
printf ("Sum | \n");
for (status = status_complete; status >= status_minimal; status--)
printf ("%s | \n",
status_color[status], status_name[status]);
printf ("
\n");
for (dev_type = type_scanner; dev_type <= type_api; dev_type++)
html_print_statistics_per_type (dev_type);
printf ("
\n");
}
/* Generate a name used for 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 ("Backends: \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 ("%s",
html_generate_anchor_name (dev_type, be->name), be->name);
}
be = be->next;
}
be = first_backend;
if (first)
printf ("(none)\n");
printf ("
\n");
while (be)
{
type_entry *type = be->type;
while (type)
{
if (type->type == dev_type)
{
mfg_entry *mfg = type->mfg;
model_entry *model;
printf ("\n");
printf ("\n");
if (be->url && be->url->name)
{
url_entry *url = be->url;
printf ("Link(s): \n");
while (url)
{
if (url != be->url)
printf (", ");
printf ("%s", url->name, url->name);
url = url->next;
}
printf ("
\n");
}
if (be->manpage)
printf ("Manual page: %s
\n", be->manpage, be->manpage);
if (be->comment)
printf ("Comment: %s
\n", be->comment);
if (type->desc)
{
if (type->desc->desc)
{
if (type->desc->url && type->desc->url->name)
printf ("Description: "
"%s
\n",
type->desc->url->name, type->desc->desc);
else
printf ("Description: %s
\n",
type->desc->desc);
}
if (type->desc->comment)
printf ("Comment: %s
\n", type->desc->comment);
printf ("
\n");
type = type->next;
continue;
}
printf ("
\n");
if (!mfg)
{
type = type->next;
continue;
}
printf ("\n");
printf ("\n");
printf ("Manufacturer | \n");
printf ("Model | \n");
printf ("Interface | \n");
printf ("USB id | \n");
printf ("Status | \n");
printf ("Comment | \n");
printf ("
\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 ("\n");
printf ("\n", num_models);
if (mfg->url && mfg->url->name)
printf ("%s\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 (" |
\n");
if (model->url && model->url->name)
printf
("%s | \n",
model->url->name, model->name);
else
printf ("%s | \n",
model->name);
if (model->interface)
printf ("%s | \n",
model->interface);
else
printf ("? | \n");
if (model->usb_vendor_id && model->usb_product_id)
printf ("%s/%s | \n",
model->usb_vendor_id, model->usb_product_id);
else
printf (" | \n");
printf ("%s | \n",
status_color[status], status_name[status]);
if (model->comment && model->comment[0] != 0)
printf ("%s | \n", model->comment);
else
printf (" | \n");
model = model->next;
printf ("
\n");
} /* while (model) */
} /* if (num_models) */
mfg = mfg->next;
} /* while (mfg) */
printf ("
\n");
} /* if (type->type) */
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
/* printf ("\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 ("Manufacturers: \n");
while (mfg_record)
{
if (mfg_record != first_mfg_record)
printf (", \n");
printf ("%s",
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 ("
\n");
while (mfg_record)
{
model_record_entry *model_record = mfg_record->model_record;
printf ("\n",
html_generate_anchor_name (type_unknown, mfg_record->name),
mfg_record->name);
printf ("\n");
if (mfg_record->url && mfg_record->url->name)
{
url_entry *url = mfg_record->url;
printf ("Link(s): \n");
while (url)
{
if (url != mfg_record->url)
printf (", ");
printf ("%s", url->name, url->name);
url = url->next;
}
printf ("
\n");
}
if (mfg_record->comment)
printf ("Comment: %s
\n", mfg_record->comment);
printf ("
\n");
if (!model_record)
{
mfg_record = mfg_record->next;
continue;
}
printf ("\n");
printf ("\n");
printf ("Model | \n");
printf ("Interface | \n");
printf ("USB id | \n");
printf ("Status | \n");
printf ("Comment | \n");
printf ("Backend | \n");
printf ("Manpage | \n");
printf ("
\n");
while (model_record)
{
enum status_entry status = model_record->status;
if (model_record->url && model_record->url->name)
printf ("%s | \n",
model_record->url->name, model_record->name);
else
printf ("
%s | \n", model_record->name);
if (model_record->interface)
printf ("%s | \n", model_record->interface);
else
printf ("? | \n");
if (model_record->usb_vendor_id && model_record->usb_product_id)
printf ("%s/%s | \n",
model_record->usb_vendor_id, model_record->usb_product_id);
else
printf (" | \n");
printf ("%s | \n",
status_color[status], status_name[status]);
if (model_record->comment && model_record->comment[0] != 0)
printf ("%s | \n", model_record->comment);
else
printf (" | \n");
printf ("\n");
if (model_record->be->url && model_record->be->url->name)
printf ("%s\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 (" (");
if (model_record->be->version)
{
printf ("%s", model_record->be->version);
if (model_record->be->new)
printf (", NEW!");
}
else
printf ("NEW!");
printf (")\n");
}
printf (" | \n");
if (model_record->be->manpage)
printf ("%s | \n",
model_record->be->manpage, model_record->be->manpage);
else
printf ("? | \n");
printf ("
\n");
model_record = model_record->next;
} /* while model_record */
printf ("
\n");
mfg_record = mfg_record->next;
} /* while mfg_record */
}
/* Print the HTML headers and an introduction */
static void
html_print_header (void)
{
printf
("\n"
" \n"
"\n");
printf ("%s\n", title);
printf
("\n"
"\n"
"\n"
"
![\"SANE\"](\"http://www.sane-project.org/images/sane.png\")
\n");
printf ("
%s
\n", title);
printf ("
\n" "
\n");
printf ("%s\n", intro);
printf
("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.
\n");
printf
("If you have new information or corrections, please file a\n"
"bug report\n"
"with as many details as possible. Also please tell us if your scanner \n"
"isn't mentioned in this list at all.
\n"
"For an explanation of the tables, see the\n"
"legend.\n");
}
/* Print the HTML footers and contact information */
static void
html_print_footer (void)
{
time_t current_time = time (0);
printf
("
\n"
"SANE homepage\n"
"\n"
"Contact\n" "\n" "\n");
printf ("This page was last updated on %s by sane-desc %s from %s\n",
asctime (localtime (¤t_time)), SANE_DESC_VERSION, PACKAGE_STRING);
printf ("\n");
printf (" \n");
}
/* print parts of the legend */
static void
html_print_legend_backend (void)
{
printf
(" Backend:\n"
" Name of the backend, in parentheses if available:\n"
" Version of backend/driver; newer versions may be\n"
" available from their home sites.
"
" NEW! means brand-new to the\n"
" current release of SANE.
\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" " \n");
}
static void
html_print_legend_link (void)
{
printf
(" Link(s):\n"
" 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
(" Manual Page:\n"
" A link to the man-page online, if it exists.\n");
}
static void
html_print_legend_comment (void)
{
printf
(" Comment:\n"
" More information about the backend or model, e.g. the level of "
" support and possible problems.\n");
}
static void
html_print_legend_manufacturer (void)
{
printf
(" Manufacturer:\n"
" Manufacturer, vendor or brand name of the device.\n");
}
static void
html_print_legend_model (void)
{
printf
(" Model:\n" " Name of the the device.\n");
}
static void
html_print_legend_interface (void)
{
printf
(" Interface:\n"
" How the device is connected to the computer.\n");
}
static void
html_print_legend_usbid (void)
{
printf
(" USB id:\n"
" The USB vendor and product ids as printed by sane-find-scanner -q (only applicable for USB devices).\n");
}
static void
html_print_legend_status (void)
{
printf
(" Status:\n"
" Indicates how many of the features the device provides \n"
" are supported by SANE.\n"
" - unsupported"
" means the device is not supported at least by this backend. "
" It may be supported by other backends, however.\n");
printf
("
- untested means the "
" device may be supported but couldn't be tested. Be very "
" careful and report success/failure.\n"
"
- minimal 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
("
- basic means it works at \n"
" least in the most important modes but quality is not perfect.\n"
"
- good means the device is usable \n"
" for day-to-day work. Some rather exotic features may be missing.\n"
"
- complete means the backends \n"
" supports everything the device can do.\n" "
\n");
}
static void
html_print_legend_description (void)
{
printf
(" Description:\n"
" 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 = " The following table summarizes the backends/drivers "
"distributed with the latest version of sane-backends, and the hardware "
"or software they support.
";
html_print_header ();
html_print_summary ();
printf ("\n");
html_backends_split_table (type_scanner);
printf ("\n");
html_backends_split_table (type_stillcam);
printf ("\n");
html_backends_split_table (type_vidcam);
printf ("\n");
html_backends_split_table (type_api);
printf ("\n");
html_backends_split_table (type_meta);
printf ("\n" "\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 ("
\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 = " The following table summarizes the devices supported "
"by the latest version of sane-backends.
";
html_print_header ();
html_print_summary ();
printf ("\n");
html_mfgs_table (type_scanner);
printf ("\n");
html_mfgs_table (type_stillcam);
printf ("\n");
html_mfgs_table (type_vidcam);
printf ("\n");
html_backends_split_table (type_api);
printf ("\n");
html_backends_split_table (type_meta);
printf
("\n" "\n" "\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 ("
\n" "
\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 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 usbid_type *
create_usbids_table (void)
{
backend_entry *be = first_backend;
usbid_type *first_usbid = 0;
while (be)
{
type_entry *type = be->type;
while (type)
{
mfg_entry *mfg = type->mfg;
model_entry *model;
if (!mfg)
{
type = type->next;
continue;
}
mfg = type->mfg;
while (mfg)
{
model = mfg->model;
if (model)
{
while (model)
{
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);
}
model = model->next;
} /* while (model) */
} /* if (model) */
mfg = mfg->next;
} /* while (mfg) */
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
return first_usbid;
}
/* 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 (¤t_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 (¤t_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"
"# 0xVVVV0xPPPProot:scanner0664[/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");
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\troot:scanner\t0664\t\n", usbid->usb_vendor_id,
usbid->usb_product_id);
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 (¤t_time)));
printf
("#\n"
"# udev rules file for supported USB devices\n"
"#\n"
"# To add a USB device, add a rule to the list below between the\n"
"# LABEL=\"libsane_rules_begin\" and LABEL=\"libsane_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 ();
int i;
print_udev_header ();
printf("ACTION!=\"add\", GOTO=\"libsane_rules_end\"\n"
"ENV{DEVTYPE}==\"usb_device\", GOTO=\"libsane_create_usb_dev\"\n"
"SUBSYSTEM==\"usb_device\", GOTO=\"libsane_rules_begin\"\n"
"SUBSYSTEM!=\"usb_device\", GOTO=\"libsane_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_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 ("SYSFS{idVendor}==\"%s\", SYSFS{idProduct}==\"%s\", MODE=\"0664\", GROUP=\"scanner\", ENV{libsane_matched}=\"yes\"\n",
usbid->usb_vendor_id + 2, usbid->usb_product_id + 2);
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_rules_end\"\n");
}
static void
print_plist (void)
{
usbid_type *usbid = create_usbids_table ();
printf ("\n");
printf ("\n");
printf ("\n");
printf ("\n");
printf ("\tdevice info version\n");
printf ("\t2.0\n");
printf ("\tusb\n");
printf ("\t\n");
printf ("\t\tIOUSBDevice\n");
printf ("\t\t\n");
while (usbid)
{
printf ("\t\t\t\n");
printf ("\t\t\t\tdevice type\n");
printf ("\t\t\t\tscanner\n");
printf ("\t\t\t\tproduct\n");
printf ("\t\t\t\t%s\n", usbid->usb_product_id);
printf ("\t\t\t\tvendor\n");
printf ("\t\t\t\t%s\n", usbid->usb_vendor_id);
printf ("\t\t\t\n");
usbid = usbid->next;
}
printf ("\t\t\n");
printf ("\t\n");
printf ("\n");
printf ("\n");
}
static void
print_hal (int new)
{
int i;
usbid_type *usbid = create_usbids_table ();
printf ("\n");
printf ("\n");
printf (" \n");
if (new)
printf (" \n");
else
printf (" \n");
while (usbid)
{
manufacturer_model_type * name = usbid->name;
i = 0;
printf (" \n");
printf (" \n", usbid->usb_vendor_id);
printf (" \n", usbid->usb_product_id);
printf (" scanner\n");
printf (" proprietary\n");
printf (" \n");
printf (" \n");
usbid = usbid->next;
}
printf (" \n");
printf (" \n");
printf ("\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;
}