kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2537 wiersze
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2537 wiersze
		
	
	
		
			66 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 "1.1"
 | |
| 
 | |
| #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 <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_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 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-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 <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") == 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 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_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_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\n",
 | |
| 			       current_level);
 | |
| 		      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'\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;
 | |
| }
 | |
| 
 | |
| /* 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: */
 | |
| /* alpha, beta, stable -> 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");
 | |
| 
 | |
|       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) */
 | |
| }
 | |
| 
 | |
| /* 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 a table of all backends providing models of type dev_type */
 | |
| static void
 | |
| html_backends_table (device_type dev_type)
 | |
| {
 | |
|   backend_entry *be = first_backend;
 | |
|   SANE_Bool found = SANE_FALSE;
 | |
| 
 | |
|   /* check if we have at least one matching backend */
 | |
|   while (be)
 | |
|     {
 | |
|       type_entry *type = be->type;
 | |
| 
 | |
|       while (type)
 | |
| 	{
 | |
| 	  if (type->type == dev_type)
 | |
| 	    found = SANE_TRUE;
 | |
| 	  type = type->next;
 | |
| 	}
 | |
|       be = be->next;
 | |
|     }
 | |
|   if (!found)
 | |
|     {
 | |
|       printf ("<p>(none)</p>\n");
 | |
|       return;
 | |
|     }
 | |
|   be = first_backend;
 | |
| 
 | |
|   printf ("<table border=1>\n");
 | |
|   printf ("<tr bgcolor=E0E0FF>\n");
 | |
| 
 | |
|   switch (dev_type)
 | |
|     {
 | |
|     case type_scanner:
 | |
|     case type_stillcam:
 | |
|     case type_vidcam:
 | |
|       printf ("<th align=center rowspan=2>Backend</th>\n");
 | |
|       printf ("<th align=center rowspan=2>Manual Page</th>\n");
 | |
|       printf ("<th align=center colspan=5>Supported Devices</th>\n");
 | |
|       printf ("</tr>\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");
 | |
|       break;
 | |
|     case type_meta:
 | |
|     case type_api:
 | |
|       printf ("<th align=center>Backend</th>\n");
 | |
|       printf ("<th align=center>Manual Page</th>\n");
 | |
|       printf ("<th align=center>Description</th>\n");
 | |
|       printf ("<th align=center>Status</th>\n");
 | |
|       printf ("<th align=center>Comment</th>\n");
 | |
|       break;
 | |
|     default:
 | |
|       DBG_ERR ("Unknown device type (%d)\n", dev_type);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   printf ("</tr>\n");
 | |
| 
 | |
|   while (be)
 | |
|     {
 | |
|       type_entry *type = be->type;
 | |
| 
 | |
|       while (type)
 | |
| 	{
 | |
| 	  if (type->type == dev_type)
 | |
| 	    {
 | |
| 	      mfg_entry *mfg = type->mfg;
 | |
| 	      model_entry *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 ("<tr><td align=center rowspan=%d>\n", row_num);
 | |
| 	      if (be->url && be->url->name)
 | |
| 		printf ("<a href=\"%s\">%s</a>\n", be->url->name, be->name);
 | |
| 	      else
 | |
| 		printf ("%s", be->name);
 | |
| 
 | |
| 	      if (be->version || be->new)
 | |
| 		{
 | |
| 		  printf ("<br>(");
 | |
| 		  if (be->version)
 | |
| 		    {
 | |
| 		      printf ("v%s", be->version);
 | |
| 		      if (be->new)
 | |
| 			printf (", <font color=" COLOR_NEW ">NEW!</font>");
 | |
| 		    }
 | |
| 		  else
 | |
| 		    printf ("<font color=" COLOR_NEW ">NEW!</font>");
 | |
| 		  printf (")\n");
 | |
| 		}
 | |
| 	      printf ("</td>\n");
 | |
| 	      if (be->manpage)
 | |
| 		printf ("<td align=center rowspan=%d><a href=\"" MAN_PAGE_LINK
 | |
| 			"\">%s</a></td>\n", row_num, be->manpage,
 | |
| 			be->manpage);
 | |
| 	      else
 | |
| 		printf ("<td align=center rowspan=%d>?</td>\n", row_num);
 | |
| 
 | |
| 	      mfg = type->mfg;
 | |
| 	      if (!mfg && type->desc)
 | |
| 		{
 | |
| 		  if (type->desc->desc)
 | |
| 		    {
 | |
| 		      if (type->desc->url && type->desc->url->name)
 | |
| 			printf ("<td><a href=\"%s\">%s</a></td>\n",
 | |
| 				type->desc->url->name, type->desc->desc);
 | |
| 		      else
 | |
| 			printf ("<td>%s</td>\n", type->desc->desc);
 | |
| 		    }
 | |
| 		  else
 | |
| 		    printf ("<td> </td>\n");
 | |
| 		  printf ("<td>");
 | |
| 		  switch (be->status)
 | |
| 		    {
 | |
| 		    case status_alpha:
 | |
| 		      printf ("<font color=" COLOR_ALPHA ">alpha</font>");
 | |
| 		      break;
 | |
| 		    case status_beta:
 | |
| 		      printf ("<font color=" COLOR_BETA ">beta</font>");
 | |
| 		      break;
 | |
| 		    case status_stable:
 | |
| 		      printf ("<font color=" COLOR_STABLE ">stable</font>");
 | |
| 		      break;
 | |
| 		    default:
 | |
| 		      printf ("?");
 | |
| 		      break;
 | |
| 		    }
 | |
| 		  printf ("</td>\n");
 | |
| 
 | |
| 		  if (type->desc->comment)
 | |
| 		    printf ("<td>%s</td>\n", type->desc->comment);
 | |
| 		  else
 | |
| 		    printf ("<td> </td>\n");
 | |
| 		  printf ("</tr>\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 ("<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_alpha:
 | |
| 			      printf ("<font color=" COLOR_ALPHA
 | |
| 				      ">alpha</font>");
 | |
| 			      break;
 | |
| 			    case status_beta:
 | |
| 			      printf ("<font color=" COLOR_BETA
 | |
| 				      ">beta</font>");
 | |
| 			      break;
 | |
| 			    case status_stable:
 | |
| 			      printf ("<font color=" COLOR_STABLE
 | |
| 				      ">stable</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) */
 | |
| 	    }			/* if (type->type) */
 | |
| 	  type = type->next;
 | |
| 	}			/* while (type) */
 | |
|       be = be->next;
 | |
|     }				/* while (be) */
 | |
|   printf ("</table>\n");
 | |
| }
 | |
| 
 | |
| /* 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 = mfg->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 ("v%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)
 | |
| 		{
 | |
| 		  printf ("<b>Status:</b> \n");
 | |
| 		  switch (be->status)
 | |
| 		    {
 | |
| 		    case status_alpha:
 | |
| 		      printf ("<font color=" COLOR_ALPHA ">alpha</font>");
 | |
| 		      break;
 | |
| 		    case status_beta:
 | |
| 		      printf ("<font color=" COLOR_BETA ">beta</font>");
 | |
| 		      break;
 | |
| 		    case status_stable:
 | |
| 		      printf ("<font color=" COLOR_STABLE ">stable</font>");
 | |
| 		      break;
 | |
| 		    default:
 | |
| 		      printf ("?");
 | |
| 		      break;
 | |
| 		    }
 | |
| 		  printf ("<br>\n");
 | |
| 
 | |
| 		  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_alpha:
 | |
| 			      printf ("<font color=" COLOR_ALPHA
 | |
| 				      ">alpha</font>");
 | |
| 			      break;
 | |
| 			    case status_beta:
 | |
| 			      printf ("<font color=" COLOR_BETA
 | |
| 				      ">beta</font>");
 | |
| 			      break;
 | |
| 			    case status_stable:
 | |
| 			      printf ("<font color=" COLOR_STABLE
 | |
| 				      ">stable</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_alpha:
 | |
| 	      printf ("<font color=" COLOR_ALPHA ">alpha</font>");
 | |
| 	      break;
 | |
| 	    case status_beta:
 | |
| 	      printf ("<font color=" COLOR_BETA ">beta</font>");
 | |
| 	      break;
 | |
| 	    case status_stable:
 | |
| 	      printf ("<font color=" COLOR_STABLE ">stable</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 ("v%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 send\n"
 | |
|      "e-mail to sane-devel, the <a\n"
 | |
|      "href=\"http://www.mostang.com/sane/mail.html\">SANE mailing\n"
 | |
|      "list</a>.</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 the HTML page with one table of backends per type */
 | |
| static void
 | |
| html_print_backends (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 ("<p><div align=center>\n");
 | |
| 
 | |
|   printf ("<h2><a name=\"SCANNERS\">Scanners</a></h2>\n");
 | |
|   html_backends_table (type_scanner);
 | |
| 
 | |
|   printf ("<h2><a name=\"STILL\">Still Cameras</a></h2>\n");
 | |
|   html_backends_table (type_stillcam);
 | |
| 
 | |
|   printf ("<h2><a name=\"VIDEO\">Video Cameras</a></h2>\n");
 | |
|   html_backends_table (type_vidcam);
 | |
| 
 | |
|   printf ("<h2><a name=\"API\">APIs</a></h2>\n");
 | |
|   html_backends_table (type_api);
 | |
| 
 | |
|   printf ("<h2><a name=\"META\">Meta Backends</a></h2>\n");
 | |
|   html_backends_table (type_meta);
 | |
| 
 | |
|   printf ("</div>\n");
 | |
| 
 | |
|   printf
 | |
|     ("<h3><a name=\"legend\">Legend:</a></h3>\n"
 | |
|      "<blockquote>\n"
 | |
|      "<dl>\n"
 | |
|      "  <dt><b>Backend:</b></dt>\n"
 | |
|      "  <dd>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.<br>"
 | |
|      "      <font color=" COLOR_NEW ">NEW!</font> means brand-new to the\n"
 | |
|      "      current release of SANE.\n" "  </dd>\n");
 | |
|   printf
 | |
|     ("  <dt><b>Manual Page:</b></dt>\n"
 | |
|      "  <dd>A link to the man-page on-line, if it exists.</dd>\n"
 | |
|      "  <dt><b>Supported Devices</b> (for hardware devices):</dt>\n"
 | |
|      "  <dd>Which hardware the backend supports.</dd>\n"
 | |
|      "  <dt><b>Manufacturer:</b></dt>\n"
 | |
|      "  <dd>Manufacturer, vendor or brand name of the device.</dd>\n"
 | |
|      "  <dt><b>Model:</b></dt>\n"
 | |
|      "  <dd>Name of the the device.</dd>\n"
 | |
|      "  <dt><b>Interface:</b></dt>\n"
 | |
|      "  <dd>How the device is connected to the computer.</dd>\n");
 | |
|   printf
 | |
|     ("  <dt><b>Status</b>:</dt>\n"
 | |
|      "  <dd>A vague indication of robustness and reliability.\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"
 | |
|      "      <li><font color=" COLOR_UNTESTED ">untested</font> means the "
 | |
|      "        device may be supported but couldn't be tested. Be very "
 | |
|      "        careful.\n");
 | |
|   printf
 | |
|     ("      <li><font color=" COLOR_ALPHA ">alpha</font> 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"
 | |
|      "      <li><font color=" COLOR_BETA ">beta</font> means it works\n"
 | |
|      "        pretty well, and looks stable and functional, but not\n"
 | |
|      "        bullet-proof.\n"
 | |
|      "      <li><font color=" COLOR_STABLE ">stable</font> means someone is\n"
 | |
|      "        pulling your leg.\n" "      </ul></dd>\n");
 | |
|   printf
 | |
|     ("  <dt><b>Comment:</b></dt>\n"
 | |
|      "  <dd>More information about the level of support and\n"
 | |
|      "      possible problems.</dd>\n"
 | |
|      "  <dt><b>Description</b> (for API and meta backends):</dt>\n"
 | |
|      "  <dd>The scope of application of the backend.\n"
 | |
|      "</dl>\n" "</blockquote>\n");
 | |
| 
 | |
|   html_print_footer ();
 | |
| }
 | |
| 
 | |
| /* 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>\n"
 | |
|      "<dl>\n"
 | |
|      "  <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.\n"
 | |
|      "  </dd>\n"
 | |
|      "  <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");
 | |
|   printf
 | |
|     ("  <dt><b>Manual Page:</b></dt>\n"
 | |
|      "  <dd>A link to the man-page on-line, if it exists.</dd>\n"
 | |
|      "  <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"
 | |
|      "  <dt><b>Manufacturer:</b></dt>\n"
 | |
|      "  <dd>Manufacturer, vendor or brand name of the device.</dd>\n"
 | |
|      "  <dt><b>Model:</b></dt>\n"
 | |
|      "  <dd>Name of the the device.</dd>\n"
 | |
|      "  <dt><b>Interface:</b></dt>\n"
 | |
|      "  <dd>How the device is connected to the computer.</dd>\n");
 | |
|   printf
 | |
|     ("  <dt><b>Status</b>:</dt>\n"
 | |
|      "  <dd>A vague indication of robustness and reliability.\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.\n"
 | |
|      "      <li><font color=" COLOR_ALPHA ">alpha</font> 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");
 | |
|   printf
 | |
|     ("      <li><font color=" COLOR_BETA ">beta</font> means it works\n"
 | |
|      "        pretty well, and looks stable and functional, but not\n"
 | |
|      "        bullet-proof.\n"
 | |
|      "      <li><font color=" COLOR_STABLE ">stable</font> means someone is\n"
 | |
|      "        pulling your leg.\n" "      </ul></dd>\n");
 | |
|   printf
 | |
|     ("  <dt><b>Description</b>:</dt>\n"
 | |
|      "  <dd>The scope of application of the backend.\n"
 | |
|      "</dl>\n" "</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"
 | |
|      "  <dt><b>Model</b>:</dt>\n"
 | |
|      "  <dd>Name of the the device.</dd>\n"
 | |
|      "  <dt><b>Interface</b>:</dt>\n"
 | |
|      "  <dd>How the device is connected to the computer.</dd>\n"
 | |
|      "  <dt><b>Status</b>:</dt>\n");
 | |
|   printf
 | |
|     ("  <dd>A vague indication of robustness and reliability.\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"
 | |
|      "      <li><font color=" COLOR_UNTESTED ">untested</font> means the "
 | |
|      "        device may be supported but couldn't be tested. Be very "
 | |
|      "        careful.\n");
 | |
|   printf
 | |
|     ("      <li><font color=" COLOR_ALPHA ">alpha</font> 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"
 | |
|      "      <li><font color=" COLOR_BETA ">beta</font> means it works\n"
 | |
|      "        pretty well, and looks stable and functional, but not\n"
 | |
|      "        bullet-proof.\n"
 | |
|      "      <li><font color=" COLOR_STABLE ">stable</font> means someone is\n"
 | |
|      "        pulling your leg.\n" "      </ul></dd>\n");
 | |
|   printf
 | |
|     ("  <dt><b>Comment</b>:</dt>\n"
 | |
|      "  <dd>More information about the level of support and\n"
 | |
|      "      possible problems.</dd>\n"
 | |
|      "  <dt><b>Backend</b>:</dt>\n"
 | |
|      "  <dd>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.<br>\n");
 | |
|   printf
 | |
|     ("      <font color=" COLOR_NEW ">NEW!</font> means brand-new to the\n"
 | |
|      "      current release of SANE.\n"
 | |
|      "  </dd>\n"
 | |
|      "  <dt><b>Manual Page</b>:</dt>\n"
 | |
|      "  <dd>A link to the man-page on-line, if it exists.</dd>\n"
 | |
|      "  <dt><b>Description</b>:</dt>\n"
 | |
|      "  <dd>The scope of application of the backend.\n"
 | |
|      "</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:
 | |
|       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;
 | |
| }
 |