/*
sane-desc.c -- generate list of supported SANE devices
Copyright (C) 2002 Henning Meier-Geinitz
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 "0.8"
#define MAN_PAGE_LINK "http://www.mostang.com/sane/man/%s.5.html"
#define COLOR_ALPHA "\"#B00000\""
#define COLOR_BETA "\"#B0B000\""
#define COLOR_STABLE "\"#008000\""
#define COLOR_UNTESTED "\"#0000B0\""
#define COLOR_UNSUPPORTED "\"#F00000\""
#define COLOR_NEW "\"#F00000\""
#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_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_alpha,
status_beta,
status_stable,
status_untested,
status_unsupported
}
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;
char *manpage;
struct url_entry *url;
char *comment;
struct type_entry *type;
SANE_Bool new;
}
backend_entry;
typedef struct backend_record_entry
{
struct backend_record_entry *next;
char *name;
char *version;
enum status_entry status;
char *manpage;
struct url_entry *url;
char *comment;
SANE_Bool new;
struct model_entry *model;
}
backend_record_entry;
typedef struct mfg_record_entry
{
struct mfg_record_entry *next;
char *name;
char *comment;
struct url_entry *url;
struct backend_record_entry *be_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-backends, 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 \n");
}
static void
print_version (void)
{
printf ("sane-desc %s (%s)\n", SANE_DESC_VERSION, PACKAGE_VERSION);
printf ("Copyright (C) 2002 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 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") == 0)
{
DBG_INFO ("Output mode: html-backends\n");
mode = output_mode_html_backends;
}
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 void
create_url_entry (url_entry ** start_entry, url_entry ** entry)
{
*entry = *start_entry;
if (*entry)
{
while ((*entry)->next)
*entry = (*entry)->next;
(*entry)->next = calloc (1, sizeof (url_entry));
(*entry) = (*entry)->next;
}
else
{
*start_entry = calloc (1, sizeof (url_entry));
*entry = *start_entry;
}
if (!*entry)
{
DBG_ERR ("calloc failed (%s)\n", strerror (errno));
exit (1);
}
return;
}
static const char *
get_string (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;
}
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_string (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_string (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;
} /* switch */
if (word)
free (word);
word = 0;
return SANE_STATUS_GOOD;
}
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_string (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 = 0;
DBG_INFO ("creating backend entry `%s'\n", string_entry);
be = calloc (1, sizeof (backend_entry));
if (!be)
{
DBG_ERR ("calloc failed (%s)\n", strerror (errno));
return SANE_FALSE;
}
be->next = first_backend;
first_backend = be;
be->name = string_entry;
be->status = status_unknown;
be->new = SANE_FALSE;
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);
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_INFO
("setting status of backend `%s' to `alpha'\n",
current_backend->name);
current_backend->status = status_alpha;
}
else if (strcmp (string_entry, ":beta") == 0)
{
DBG_INFO
("setting status of backend `%s' to `beta'\n",
current_backend->name);
current_backend->status = status_beta;
}
else if (strcmp (string_entry, ":stable") == 0)
{
DBG_INFO
("setting status of backend `%s' to `stable'\n",
current_backend->name);
current_backend->status = status_stable;
}
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'\n",
current_model->name);
if (strcmp (string_entry, ":alpha") == 0)
{
DBG_INFO
("setting status of model `%s' to `alpha'\n",
current_model->name);
current_model->status = status_alpha;
}
else if (strcmp (string_entry, ":beta") == 0)
{
DBG_INFO
("setting status of model `%s' to `beta'\n",
current_model->name);
current_model->status = status_beta;
}
else if (strcmp (string_entry, ":stable") == 0)
{
DBG_INFO
("setting status of model `%s' to `stable'\n",
current_model->name);
current_model->status = status_stable;
}
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'\n",
current_model->name, string_entry);
current_model->status = status_unknown;
return SANE_FALSE;
}
break;
default:
DBG_ERR ("level %d not implemented for :status\n",
current_level);
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\n");
return SANE_FALSE;
}
if (current_type->type < type_meta)
{
DBG_ERR ("use `:desc' for `:api' and `:meta' only\n");
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\n");
return SANE_FALSE;
}
if (current_type->type >= type_meta)
{
DBG_ERR ("use `:mfg' for hardware devices only\n");
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\n");
return SANE_FALSE;
}
if (current_level != level_mfg
&& current_level != level_model)
{
DBG_ERR ("use `:mfg' keyword first\n");
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 interface of model "
"`%s' to `%s` (was: `%s')\n",
current_model->name, string_entry,
current_type->desc);
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)
{
url_entry *url = 0;
switch (current_level)
{
case level_backend:
create_url_entry (¤t_backend->url, &url);
DBG_INFO ("adding `%s' to list of urls of backend "
"`%s'\n", string_entry,
current_backend->name);
break;
case level_mfg:
create_url_entry (¤t_mfg->url, &url);
DBG_INFO ("adding `%s' to list of urls of mfg "
"`%s'\n", string_entry, current_mfg->name);
break;
case level_desc:
create_url_entry (¤t_type->desc->url, &url);
DBG_INFO ("adding `%s' to list of urls of description "
"for backend `%s'\n", string_entry,
current_backend->name);
break;
case level_model:
create_url_entry (¤t_model->url, &url);
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\n",
current_level);
return SANE_FALSE;
}
url->name = string_entry;
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'\n",
current_level);
return SANE_FALSE;
}
continue;
}
DBG_ERR ("unknown keyword token in line `%s'\n", line);
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;
}
static void
sort_by_backend (void)
{
backend_entry *last_be = 0, *be = first_backend;
backend_entry *next_be = first_backend->next;
SANE_Bool changed = SANE_FALSE;
do /* bubblesort */
{
changed = SANE_FALSE;
last_be = 0;
be = first_backend;
next_be = first_backend->next;
while (be->next)
{
if (strcasecmp (be->name, be->next->name) > 0)
{
backend_entry *a = be, *b = be->next, *c = be->next->next;
be = b;
if (last_be)
last_be->next = b;
else
first_backend = b;
be->next = a;
a->next = c;
changed = SANE_TRUE;
}
last_be = be;
be = be->next;
}
}
while (changed);
}
static mfg_record_entry *
sort_by_mfg (device_type dev_type)
{
mfg_record_entry *first_mfg_record = 0, *mfg_record = 0;
backend_entry *be = first_backend;
mfg_record_entry *last_mfg_record = 0;
mfg_record_entry *next_mfg_record = first_mfg_record->next;
backend_record_entry *be_record = 0;
SANE_Bool changed = SANE_FALSE;
DBG_DBG ("sort_by_mfg: start\n");
while (be)
{
type_entry *type = be->type;
while (type)
{
if (type->type == dev_type)
{
mfg_entry *mfg = type->mfg;
while (mfg)
{
mfg_record = first_mfg_record;
while (mfg_record)
{
if (strcasecmp (mfg_record->name, mfg->name) == 0)
{
url_entry *mfg_url = mfg->url;
be_record = mfg_record->be_record;
if (be_record)
{
while (be_record->next)
be_record = be_record->next;
be_record->next
= calloc (1, sizeof (backend_record_entry));
be_record = be_record->next;
}
else
{
mfg_record->be_record
= calloc (1, sizeof (backend_record_entry));
be_record = mfg_record->be_record;
}
if (!be_record)
{
DBG_ERR ("sort_by_mfg: couldn't calloc "
"backend_record_entry\n");
}
/* manufacturer comments and (additional) URLs? */
if (!mfg_record->comment)
mfg_record->comment = mfg->comment;
while (mfg_url && mfg_url->name)
{
url_entry *mfg_record_url = mfg_record->url;
SANE_Bool found = SANE_FALSE;
while (mfg_record_url && mfg_record_url->name)
{
if (strcasecmp
(mfg_url->name,
mfg_record_url->name) == 0)
found = SANE_TRUE;
mfg_record_url = mfg_record_url->next;
}
if (!found)
{
mfg_record_url = mfg_record->url;
if (mfg_record_url)
{
while (mfg_record_url->next)
mfg_record_url = mfg_record_url->next;
mfg_record_url->next =
calloc (1, sizeof (url_entry));
mfg_record_url = mfg_record_url->next;
}
else
{
mfg_record->url =
calloc (1, sizeof (url_entry));
mfg_record_url = mfg_record->url;
}
if (!mfg_record_url)
{
DBG_ERR ("sort_by_mfg: couldn't calloc "
"url_entry\n");
}
mfg_record_url->name = mfg_url->name;
}
mfg_url = mfg_url->next;
}
break;
} /* if (strcmp) */
mfg_record = mfg_record->next;
} /* while (mfg_record) */
if (!mfg_record)
{
url_entry *url = mfg->url;
url_entry *current_url = 0;
mfg_record = calloc (1, sizeof (mfg_record_entry));
if (!mfg_record)
{
DBG_ERR ("sort_by_mfg: couldn't calloc "
"mfg_record_entry\n");
}
mfg_record->name = mfg->name;
mfg_record->comment = mfg->comment;
while (url)
{
if (!mfg_record->url)
{
mfg_record->url =
calloc (1, sizeof (url_entry));
current_url = mfg_record->url;
}
else
{
current_url->next =
calloc (1, sizeof (url_entry));
current_url = current_url->next;
}
if (!current_url)
{
DBG_ERR ("sort_by_mfg: couldn't calloc "
"url_entry\n");
}
current_url->name = url->name;
url = url->next;
}
mfg_record->be_record
= calloc (1, sizeof (backend_record_entry));
if (!mfg_record->be_record)
{
DBG_ERR ("sort_by_mfg: couldn't calloc "
"backend_record_entry\n");
}
be_record = mfg_record->be_record;
if (first_mfg_record)
{
mfg_record_entry *tmp_mfg_record = first_mfg_record;
while (tmp_mfg_record->next)
tmp_mfg_record = tmp_mfg_record->next;
tmp_mfg_record->next = mfg_record;
}
else
first_mfg_record = mfg_record;
DBG_DBG ("sort_by_mfg: created mfg %s\n",
mfg_record->name);
} /* if (!mfg_record) */
/* create be entries */
be_record->name = be->name;
be_record->version = be->version;
be_record->status = be->status;
be_record->manpage = be->manpage;
be_record->url = be->url;
be_record->comment = be->comment;
be_record->new = be->new;
be_record->model = mfg->model;
DBG_DBG ("sort_by_mfg: added be %s to mfg %s\n",
be_record->name, mfg->name);
mfg = mfg->next;
} /* while (mfg) */
} /* if (type) */
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
DBG_DBG ("sort_by_mfg: Sorting manufacturers\n");
if (!first_mfg_record)
{
DBG_DBG ("sort_by_mfg: no manufacturers found\n");
return 0;
}
do
{ /* bubblesort for manufacturers */
changed = SANE_FALSE;
last_mfg_record = 0;
mfg_record = first_mfg_record;
next_mfg_record = first_mfg_record->next;
while (mfg_record->next)
{
DBG_DBG("%s, %s\n", mfg_record->name, mfg_record->next->name);
if (strcasecmp (mfg_record->name, mfg_record->next->name) > 0)
{
mfg_record_entry *a = mfg_record, *b = mfg_record->next,
*c = mfg_record->next->next;
mfg_record = b;
if (last_mfg_record)
last_mfg_record->next = b;
else
first_mfg_record = b;
mfg_record->next = a;
a->next = c;
changed = SANE_TRUE;
DBG_DBG("chnaged: %s, %s\n", mfg_record->name,
mfg_record->next->name);
}
last_mfg_record = mfg_record;
mfg_record = mfg_record->next;
}
}
while (changed);
DBG_DBG ("sort_by_mfg: exit\n");
return first_mfg_record;
}
static void
ascii_print_backends (void)
{
backend_entry *be;
sort_by_backend ();
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");
switch (be->status)
{
case status_alpha:
printf (" status alpha\n");
break;
case status_beta:
printf (" status beta\n");
break;
case status_stable:
printf (" status stable\n");
break;
default:
printf (" status *unknown*\n");
break;
}
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");
switch (model->status)
{
case status_alpha:
printf (" status alpha\n");
break;
case status_beta:
printf (" status beta\n");
break;
case status_stable:
printf (" status stable\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 *
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;
}
static void
html_backends_table (device_type dev_type)
{
backend_entry *be = first_backend;
printf ("\n");
printf ("\n");
switch (dev_type)
{
case type_scanner:
case type_stillcam:
case type_vidcam:
printf ("Backend | \n");
printf ("Manual Page | \n");
printf ("Supported Devices | \n");
printf ("
\n");
printf ("\n");
printf ("Manufacturer | \n");
printf ("Model | \n");
printf ("Interface | \n");
printf ("Status | \n");
printf ("Comment | \n");
break;
case type_meta:
case type_api:
printf ("Backend | \n");
printf ("Manual Page | \n");
printf ("Description | \n");
printf ("Status | \n");
printf ("Comment | \n");
break;
default:
DBG_ERR ("Unknown device type (%d)\n", dev_type);
return;
}
printf ("
\n");
while (be)
{
type_entry *type = be->type;
while (type)
{
if (type->type == dev_type)
{
mfg_entry *mfg = type->mfg;
model_entry *model = mfg->model;
int row_num = 0;
/* count models for backend rowspan */
if (mfg) /* scanner, camera */
while (mfg)
{
model = mfg->model;
while (model)
{
model = model->next;
row_num++;
}
mfg = mfg->next;
}
else
row_num = 1;
printf ("\n", row_num);
if (be->url && be->url->name)
printf ("%s\n", be->url->name, be->name);
else
printf ("%s", be->name);
if (be->version || be->new)
{
printf (" (");
if (be->version)
{
printf ("v%s", be->version);
if (be->new)
printf (", NEW!");
}
else
printf ("NEW!");
printf (")\n");
}
printf (" | \n");
if (be->manpage)
printf ("%s | \n", row_num, be->manpage,
be->manpage);
else
printf ("? | \n", row_num);
mfg = type->mfg;
if (!mfg && type->desc)
{
if (type->desc->desc)
{
if (type->desc->url && type->desc->url->name)
printf ("%s | \n",
type->desc->url->name, type->desc->desc);
else
printf ("%s | \n", type->desc->desc);
}
else
printf (" | \n");
printf ("");
switch (be->status)
{
case status_alpha:
printf ("alpha");
break;
case status_beta:
printf ("beta");
break;
case status_stable:
printf ("stable");
break;
default:
printf ("?");
break;
}
printf (" | \n");
if (type->desc->comment)
printf ("%s | \n", type->desc->comment);
else
printf (" | \n");
printf ("
\n");
}
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;
if (mfg != type->mfg)
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");
printf ("");
if (status == status_unknown)
status = be->status;
switch (status)
{
case status_alpha:
printf ("alpha");
break;
case status_beta:
printf ("beta");
break;
case status_stable:
printf ("stable");
break;
case status_untested:
printf ("untested");
break;
case status_unsupported:
printf ("unsupported");
break;
default:
printf ("?");
break;
}
printf (" | \n");
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) */
} /* if (type->type) */
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
printf ("
\n");
}
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 (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 = mfg->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)
{
printf ("Status: \n");
switch (be->status)
{
case status_alpha:
printf ("alpha");
break;
case status_beta:
printf ("beta");
break;
case status_stable:
printf ("stable");
break;
default:
printf ("?");
break;
}
printf ("
\n");
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 ("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");
printf ("");
if (status == status_unknown)
status = be->status;
switch (status)
{
case status_alpha:
printf ("alpha");
break;
case status_beta:
printf ("beta");
break;
case status_stable:
printf ("stable");
break;
case status_untested:
printf ("untested");
break;
case status_unsupported:
printf ("unsupported");
break;
default:
printf ("?");
break;
}
printf (" | \n");
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) */
} /* if (type->type) */
printf ("
\n");
type = type->next;
} /* while (type) */
be = be->next;
} /* while (be) */
printf ("\n");
}
static void
html_mfgs_table (device_type dev_type)
{
mfg_record_entry *mfg_record = 0, *first_mfg_record = 0;
first_mfg_record = sort_by_mfg (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 (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)
{
backend_record_entry *be_record = mfg_record->be_record;
printf ("\n",
html_generate_anchor_name (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 (!be_record || !be_record->model)
{
mfg_record = mfg_record->next;
continue;
}
printf ("\n");
printf ("\n");
printf ("Model | \n");
printf ("Interface | \n");
printf ("Status | \n");
printf ("Comment | \n");
printf ("Backend | \n");
printf ("Manpage | \n");
printf ("
\n");
while (be_record)
{
model_entry *model = be_record->model;
while (model)
{
enum status_entry status = model->status;
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");
printf ("");
if (status == status_unknown)
status = be_record->status;
switch (status)
{
case status_alpha:
printf ("alpha");
break;
case status_beta:
printf ("beta");
break;
case status_stable:
printf ("stable");
break;
case status_untested:
printf ("untested");
break;
case status_unsupported:
printf ("unsupported");
break;
default:
printf ("?");
break;
}
printf (" | \n");
if (model->comment && model->comment[0] != 0)
printf ("%s | \n", model->comment);
else
printf (" | \n");
printf ("\n");
if (be_record->url && be_record->url->name)
printf ("%s\n",
be_record->url->name, be_record->name);
else
printf ("%s", be_record->name);
if (be_record->version || be_record->new)
{
printf (" (");
if (be_record->version)
{
printf ("v%s", be_record->version);
if (be_record->new)
printf (", NEW!");
}
else
printf ("NEW!");
printf (")\n");
}
printf (" | \n");
if (be_record->manpage)
printf ("%s | \n",
be_record->manpage, be_record->manpage);
else
printf ("? | \n");
printf ("
\n");
model = model->next;
} /* while model */
be_record = be_record->next;
} /* while be_record */
printf ("
\n");
mfg_record = mfg_record->next;
} /* while mfg_record */
}
static void
html_print_header (void)
{
printf
("\n"
" \n"
"\n");
printf ("%s\n", title);
printf
("\n"
"\n"
"\n"
"
\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"
"There are special tables for parallel port and \n"
"USB scanners from \n"
"Jonathan Buzzard.
\n"
"If you have new information or corrections, please send\n"
"e-mail to sane-devel, the SANE mailing\n"
"list.
\n"
"For an explanation of the tables, see the\n"
"legend.\n"
"
There are tables for scanners,\n"
"still cameras,\n"
"video cameras,\n"
"APIs, and\n"
"meta backends.\n");
}
static void
html_print_footer (void)
{
time_t current_time = time (0);
printf
("
\n"
"[Back]\n"
"\n"
"sane-devel@mostang.com / SANE Development mailing list\n"
"\n" "\n");
printf ("This page was last updated on %s\n",
asctime (localtime (¤t_time)));
printf ("\n");
printf (" \n");
}
static void
html_print_backends (void)
{
backend_entry *be;
sort_by_backend ();
be = first_backend;
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 ();
printf ("\n");
printf ("
\n");
html_backends_table (type_scanner);
printf ("
\n");
html_backends_table (type_stillcam);
printf ("
\n");
html_backends_table (type_vidcam);
printf ("
\n");
html_backends_table (type_api);
printf ("
\n");
html_backends_table (type_meta);
printf ("
\n");
printf
("\n"
"\n"
"\n"
" - Backend:
\n"
" - Name of the backend, with a link to more extensive and\n"
" detailed information, if it exists, or the email address\n"
" of the author or maintainer. 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"
" \n"
" - Manual Page:
\n"
" - A link to the man-page on-line, if it exists.
\n"
" - Supported Devices (for hardware devices):
\n"
" - Which hardware the backend supports.
\n"
" - Manufacturer:
\n"
" - Manufacturer, vendor or brand name of the device.
\n"
" - Model:
\n"
" - Name of the the device.
\n"
" - Interface:
\n"
" - How the device is connected to the computer.
\n"
" - Status:
\n"
" - A vague indication of robustness and reliability.\n"
"
- unsupported"
" means the device is not supported at least by this backend. "
" It may be supported by other backends, however.\n"
"
- untested means the "
" device may be supported but couldn't be tested. Be very "
" careful.\n"
"
- alpha means it must\n"
" do something, but is not very well tested, probably has\n"
" bugs, and may even crash your system, etc., etc.\n"
"
- beta means it works\n"
" pretty well, and looks stable and functional, but not\n"
" bullet-proof.\n"
"
- stable means someone is\n"
" pulling your leg.\n"
"
\n"
" - Comment:
\n"
" - More information about the level of support and\n"
" possible problems.
\n"
" - Description (for API and meta backends):
\n"
" - The scope of application of the backend.\n"
"
\n" "
\n");
html_print_footer ();
}
static void
html_print_backends_split (void)
{
backend_entry *be;
sort_by_backend ();
be = first_backend;
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 ();
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"
"\n"
" - 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"
" \n"
" - 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"
"
- Manual Page:
\n"
" - A link to the man-page on-line, if it exists.
\n"
" - Comment:
\n"
" - More information about the backend or model, e.g. the level of "
" support and possible problems.
\n"
" - Manufacturer:
\n"
" - Manufacturer, vendor or brand name of the device.
\n"
" - Model:
\n"
" - Name of the the device.
\n"
" - Interface:
\n"
" - How the device is connected to the computer.
\n"
" - Status:
\n"
" - A vague indication of robustness and reliability.\n"
"
- unsupported"
" means the device is not supported at least by this backend. "
" It may be supported by other backends, however.\n"
"
- untested means the "
" device may be supported but couldn't be tested. Be very "
" careful.\n"
"
- alpha means it must\n"
" do something, but is not very well tested, probably has\n"
" bugs, and may even crash your system, etc., etc.\n"
"
- beta means it works\n"
" pretty well, and looks stable and functional, but not\n"
" bullet-proof.\n"
"
- stable means someone is\n"
" pulling your leg.\n"
"
\n"
" - Description:
\n"
" - The scope of application of the backend.\n"
"
\n" "
\n");
html_print_footer ();
}
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.
";
sort_by_backend ();
html_print_header ();
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"
" - Model:
\n"
" - Name of the the device.
\n"
" - Interface:
\n"
" - How the device is connected to the computer.
\n"
" - Status:
\n"
" - A vague indication of robustness and reliability.\n"
"
- unsupported"
" means the device is not supported at least by this backend. "
" It may be supported by other backends, however.\n"
"
- untested means the "
" device may be supported but couldn't be tested. Be very "
" careful.\n"
"
- alpha means it must\n"
" do something, but is not very well tested, probably has\n"
" bugs, and may even crash your system, etc., etc.\n"
"
- beta means it works\n"
" pretty well, and looks stable and functional, but not\n"
" bullet-proof.\n"
"
- stable means someone is\n"
" pulling your leg.\n"
"
\n"
" - Comment:
\n"
" - More information about the level of support and\n"
" possible problems.
\n"
" - Backend:
\n"
" - Name of the backend, with a link to more extensive and\n"
" detailed information, if it exists, or the email address\n"
" of the author or maintainer. In parentheses if available:\n"
" Version of backend/driver; newer versions may be\n"
" available from their home sites.
\n"
" NEW! means brand-new to the\n"
" current release of SANE.\n"
" \n"
" - Manual Page:
\n"
" - A link to the man-page on-line, if it exists.
\n"
" - Description:
\n"
" - The scope of application of the backend.\n"
"
\n" "
\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:
html_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;
}