kopia lustrzana https://gitlab.com/sane-project/backends
2256 wiersze
57 KiB
C
2256 wiersze
57 KiB
C
/*
|
|
sane-desc.c -- generate list of supported SANE devices
|
|
|
|
Copyright (C) 2002, 2003 Henning Meier-Geinitz <henning@meier-geinitz.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.
|
|
*/
|
|
|
|
#define SANE_DESC_VERSION "2.0"
|
|
|
|
#define MAN_PAGE_LINK "http://www.mostang.com/sane/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\""
|
|
|
|
#include <../include/sane/config.h>
|
|
|
|
#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"
|
|
|
|
#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_html_backends,
|
|
output_mode_html_backends_split,
|
|
output_mode_html_mfgs
|
|
}
|
|
output_mode;
|
|
|
|
typedef enum parameter_type
|
|
{
|
|
param_none = 0,
|
|
param_string
|
|
}
|
|
parameter_type;
|
|
|
|
typedef enum status_entry
|
|
{
|
|
status_unknown,
|
|
status_untested,
|
|
status_unsupported,
|
|
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;
|
|
}
|
|
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;
|
|
enum status_entry status; /* deprecated */
|
|
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;
|
|
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;
|
|
|
|
|
|
static char *program_name;
|
|
static int debug = 0;
|
|
static int current_debug_level = 0;
|
|
static char *search_dir = 0;
|
|
static backend_entry *first_backend = 0;
|
|
static enum output_mode mode = output_mode_ascii;
|
|
static char *title = 0;
|
|
static char *intro = 0;
|
|
|
|
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;
|
|
}
|
|
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");
|
|
printf
|
|
(" -m|--mode mode Output mode (ascii, html-backends-split,\n"
|
|
" html-mfgs)\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 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 files 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 = strdup (optarg);
|
|
DBG_INFO ("setting search directory to `%s'\n", search_dir);
|
|
break;
|
|
case 'm':
|
|
if (strcmp (optarg, "ascii") == 0)
|
|
{
|
|
DBG_INFO ("Output mode: ascii\n");
|
|
mode = output_mode_ascii;
|
|
}
|
|
else if (strcmp (optarg, "html-backends-split") == 0)
|
|
{
|
|
DBG_INFO ("Output mode: html-backends-split\n");
|
|
mode = output_mode_html_backends_split;
|
|
}
|
|
else if (strcmp (optarg, "html-mfgs") == 0)
|
|
{
|
|
DBG_INFO ("Output mode: html-mfgs\n");
|
|
mode = output_mode_html_mfgs;
|
|
}
|
|
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)
|
|
search_dir = ".";
|
|
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;
|
|
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 = 0;
|
|
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 (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);
|
|
/* 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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/* 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[PATH_MAX], *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;
|
|
|
|
DBG_INFO ("looking for .desc files in `%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;
|
|
}
|
|
current_backend = 0;
|
|
current_type = 0;
|
|
current_mfg = 0;
|
|
current_model = 0;
|
|
while (sanei_config_read (line, sizeof (line), fp))
|
|
{
|
|
char *string_entry = 0;
|
|
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->status = status_unknown;
|
|
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_backend:
|
|
if (current_backend->status != status_unknown)
|
|
{
|
|
DBG_WARN ("overwriting status of backend `%s'\n",
|
|
current_backend->name);
|
|
}
|
|
if (strcmp (string_entry, ":new") == 0)
|
|
{
|
|
DBG_WARN ("ignored `%s' status :new, use keyword "
|
|
"`:new :yes' instead\n",
|
|
current_backend->name);
|
|
current_backend->status = status_unknown;
|
|
}
|
|
else if (strcmp (string_entry, ":alpha") == 0)
|
|
{
|
|
DBG_WARN
|
|
("DEPRECATED backend status `alpha': setting status of backend `%s' to `basic'\n",
|
|
current_backend->name);
|
|
current_backend->status = status_basic;
|
|
}
|
|
else if (strcmp (string_entry, ":beta") == 0)
|
|
{
|
|
DBG_WARN
|
|
("DEPRECATED backend status `beta': setting status of backend `%s' to `good'\n",
|
|
current_backend->name);
|
|
current_backend->status = status_good;
|
|
}
|
|
else if (strcmp (string_entry, ":stable") == 0)
|
|
{
|
|
DBG_WARN
|
|
("DEPRECATED backend status `stable': setting status of backend `%s' to `complete'\n",
|
|
current_backend->name);
|
|
current_backend->status = status_complete;
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR ("unknown status of backend `%s': `%s'\n",
|
|
current_backend->name, string_entry);
|
|
current_backend->status = status_unknown;
|
|
return SANE_FALSE;
|
|
}
|
|
break;
|
|
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, ":alpha") == 0)
|
|
{
|
|
DBG_WARN
|
|
("DEPRECATED status `alpha': setting status of model `%s' to `basic' (backend `%s')\n",
|
|
current_model->name, current_backend->name);
|
|
current_model->status = status_basic;
|
|
}
|
|
else if (strcmp (string_entry, ":beta") == 0)
|
|
{
|
|
DBG_WARN
|
|
("DEPRECATED status `beta': setting status of model `%s' to `good' (backend `%s')\n",
|
|
current_model->name, current_backend->name);
|
|
current_model->status = status_good;
|
|
}
|
|
else if (strcmp (string_entry, ":stable") == 0)
|
|
{
|
|
DBG_WARN
|
|
("DEPRECATED status `stable': setting status of model `%s' to `complete' (backend `%s')\n",
|
|
current_model->name, current_backend->name);
|
|
current_model->status = status_complete;
|
|
}
|
|
else 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, ":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 (!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;
|
|
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)
|
|
{
|
|
/* 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->status == status_unknown)
|
|
model->status = be->status;
|
|
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) */
|
|
}
|
|
|
|
/* Generate a name used for <a name=...> HTML tags */
|
|
static char *
|
|
html_generate_anchor_name (char *manufacturer_name)
|
|
{
|
|
char *name = strdup (manufacturer_name);
|
|
char *pointer = name;
|
|
|
|
if (!name)
|
|
{
|
|
DBG_DBG ("html_generate_anchor_name: couldn't strdup\n");
|
|
}
|
|
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 (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 (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>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");
|
|
|
|
printf ("<td align=center>");
|
|
if (status == status_unknown)
|
|
status = be->status;
|
|
switch (status)
|
|
{
|
|
case status_minimal:
|
|
printf ("<font color=" COLOR_MINIMAL
|
|
">minimal</font>");
|
|
break;
|
|
case status_basic:
|
|
printf ("<font color=" COLOR_BASIC
|
|
">basic</font>");
|
|
break;
|
|
case status_good:
|
|
printf ("<font color=" COLOR_GOOD
|
|
">good</font>");
|
|
break;
|
|
case status_complete:
|
|
printf ("<font color=" COLOR_COMPLETE
|
|
">complete</font>");
|
|
break;
|
|
case status_untested:
|
|
printf ("<font color=" COLOR_UNTESTED
|
|
">untested</font>");
|
|
break;
|
|
case status_unsupported:
|
|
printf ("<font color=" COLOR_UNSUPPORTED
|
|
">unsupported</font>");
|
|
break;
|
|
default:
|
|
printf ("?");
|
|
break;
|
|
}
|
|
printf ("</td>\n");
|
|
|
|
if (model->comment && model->comment[0] != 0)
|
|
printf ("<td>%s</td>\n", model->comment);
|
|
else
|
|
printf ("<td> </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 (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 (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>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");
|
|
|
|
printf ("<td align=center>");
|
|
|
|
if (status == status_unknown)
|
|
status = model_record->be->status;
|
|
|
|
switch (status)
|
|
{
|
|
case status_minimal:
|
|
printf ("<font color=" COLOR_MINIMAL ">minimal</font>");
|
|
break;
|
|
case status_basic:
|
|
printf ("<font color=" COLOR_BASIC ">basic</font>");
|
|
break;
|
|
case status_good:
|
|
printf ("<font color=" COLOR_GOOD ">good</font>");
|
|
break;
|
|
case status_complete:
|
|
printf ("<font color=" COLOR_COMPLETE ">complete</font>");
|
|
break;
|
|
case status_untested:
|
|
printf ("<font color=" COLOR_UNTESTED ">untested</font>");
|
|
break;
|
|
case status_unsupported:
|
|
printf ("<font color=" COLOR_UNSUPPORTED ">unsupported</font>");
|
|
break;
|
|
default:
|
|
printf ("?");
|
|
break;
|
|
}
|
|
printf ("</td>\n");
|
|
|
|
if (model_record->comment && model_record->comment[0] != 0)
|
|
printf ("<td>%s</td>\n", model_record->comment);
|
|
else
|
|
printf ("<td> </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.mostang.com/sane/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"
|
|
"<p>There are special tables for <a\n"
|
|
"href=\"http://www.buzzard.org.uk/jonathan/scanners.html\"\n"
|
|
">parallel port</a> and <a\n"
|
|
"href=\"http://www.buzzard.org.uk/jonathan/scanners-usb.html\">\n"
|
|
"USB</a> scanners from <a\n"
|
|
"href=\"mailto:jonathan@buzzard.org.uk\">\n"
|
|
"Jonathan Buzzard</a>.</p>\n");
|
|
printf
|
|
("<p>If you have new information or corrections, please contact\n"
|
|
"<a href=\"mailto:henning@meier-geinitz.de\">henning@meier-geinitz.de</a>\n"
|
|
"with as many details as possible. Also please contact me 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");
|
|
printf
|
|
("<p>There are tables for <a href=\"#SCANNERS\">scanners</a>,\n"
|
|
"<a href=\"#STILL\">still cameras</a>,\n"
|
|
"<a href=\"#VIDEO\">video cameras</a>,\n"
|
|
"<a href=\"#API\">APIs</a>, and\n"
|
|
"<a href=\"#META\">meta backends</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.mostang.com/sane/\">[Back]</a>\n"
|
|
"<address>\n"
|
|
"<a href=\"http://www.mostang.com/sane/mail.html\"\n"
|
|
">sane-devel@mostang.com</a> / SANE Development mailing list\n"
|
|
"</address>\n" "<font size=-1>\n");
|
|
printf ("This page was last updated on %s\n",
|
|
asctime (localtime (¤t_time)));
|
|
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_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 ();
|
|
|
|
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_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 ();
|
|
|
|
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_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 ();
|
|
}
|
|
|
|
|
|
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_html_backends_split:
|
|
html_print_backends_split ();
|
|
break;
|
|
case output_mode_html_mfgs:
|
|
html_print_mfgs ();
|
|
break;
|
|
default:
|
|
DBG_ERR ("Unknown output mode\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|