• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* sane - Scanner Access Now Easy.
2    Copyright (C) 1997 Jeffrey S. Freedman
3    This file is part of the SANE package.
4 
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 
18    As a special exception, the authors of SANE give permission for
19    additional uses of the libraries contained in this release of SANE.
20 
21    The exception is that, if you link a SANE library with other files
22    to produce an executable, this does not by itself cause the
23    resulting executable to be covered by the GNU General Public
24    License.  Your use of that executable is in no way restricted on
25    account of linking the SANE library code into it.
26 
27    This exception does not, however, invalidate any other reasons why
28    the executable file might be covered by the GNU General Public
29    License.
30 
31    If you submit changes to SANE to the maintainers to be included in
32    a subsequent release, you agree by submitting the changes that
33    those changes may be distributed with this exception intact.
34 
35    If you write modifications of your own for SANE, it is your choice
36    whether to permit this exception to apply to your modifications.
37    If you do not wish that, delete this exception notice.
38 
39    This file provides generic configuration support.  */
40 
41 #include "../include/sane/config.h"
42 
43 #include <ctype.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include <sys/param.h>
49 
50 #include "../include/sane/sanei.h"
51 #include "../include/sane/sanei_config.h"
52 
53 #define BACKEND_NAME	sanei_config
54 #include "../include/sane/sanei_debug.h"
55 
56 #ifndef PATH_MAX
57 # define PATH_MAX	1024
58 #endif
59 
60 #if defined(_WIN32) || defined(HAVE_OS2_H)
61 # define DIR_SEP	";"
62 # define PATH_SEP	'\\'
63 #else
64 # define DIR_SEP	":"
65 # define PATH_SEP	'/'
66 #endif
67 
68 #define DEFAULT_DIRS	"." DIR_SEP STRINGIFY(PATH_SANE_CONFIG_DIR)
69 
70 #ifdef __BEOS__
71 #include <FindDirectory.h>
72 #endif
73 
74 static char *dir_list;
75 
76 const char *
sanei_config_get_paths()77 sanei_config_get_paths ()
78 {
79 #ifdef __BEOS__
80   char result[PATH_MAX];
81 #endif
82   void *mem;
83   char *dlist;
84   size_t len;
85 
86   if (!dir_list)
87     {
88       DBG_INIT();
89 
90       dlist = getenv ("SANE_CONFIG_DIR");
91       if (dlist)
92 	dir_list = strdup (dlist);
93 #ifdef __BEOS__
94       /* ~/config/settings/SANE takes precedence over /etc/sane.d/ */
95       if (!dir_list)
96 	{
97 	  if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, true, result, PATH_MAX) == B_OK)
98 	    {
99 	      strcat(result,"/SANE");
100 	      strcat(result,DIR_SEP); /* do append the default ones */
101 	      dir_list = strdup (result);
102 	    }
103 	}
104 #endif
105       if (dir_list)
106 	{
107 	  len = strlen (dir_list);
108 	  if ((len > 0) && (dir_list[len - 1] == DIR_SEP[0]))
109 	    {
110 	      /* append default search directories: */
111 	      mem = malloc (len + sizeof (DEFAULT_DIRS));
112 	      memcpy (mem, dir_list, len);
113 	      memcpy ((char *) mem + len, DEFAULT_DIRS, sizeof (DEFAULT_DIRS));
114 	      free (dir_list);
115 	      dir_list = mem;
116 	    }
117 	}
118       else
119 	{
120 	  /* Create a copy, since we might call free on it */
121 	  dir_list = strdup (DEFAULT_DIRS);
122 	}
123     }
124   DBG (5, "sanei_config_get_paths: using config directories  %s\n", dir_list);
125 
126   return dir_list;
127 }
128 
129 FILE *
sanei_config_open(const char * filename)130 sanei_config_open (const char *filename)
131 {
132   char *next, *dir, result[PATH_MAX];
133   const char *cfg_dir_list;
134   FILE *fp;
135   char *copy;
136 
137   cfg_dir_list = sanei_config_get_paths ();
138   if (!cfg_dir_list)
139     {
140       DBG(2, "sanei_config_open: could not find config file `%s'\n", filename);
141       return NULL;
142     }
143 
144   copy = strdup (cfg_dir_list);
145 
146   for (next = copy; (dir = strsep (&next, DIR_SEP)) != 0; )
147     {
148       snprintf (result, sizeof (result), "%s%c%s", dir, PATH_SEP, filename);
149       DBG(4, "sanei_config_open: attempting to open `%s'\n", result);
150       fp = fopen (result, "r");
151       if (fp)
152 	{
153 	  DBG(3, "sanei_config_open: using file `%s'\n", result);
154 	  break;
155 	}
156     }
157   free (copy);
158 
159   if (!fp)
160     DBG(2, "sanei_config_open: could not find config file `%s'\n", filename);
161 
162   return fp;
163 }
164 
165 const char *
sanei_config_skip_whitespace(const char * str)166 sanei_config_skip_whitespace (const char *str)
167 {
168   while (str && *str && isspace (*str))
169     ++str;
170   return str;
171 }
172 
173 const char *
sanei_config_get_string(const char * str,char ** string_const)174 sanei_config_get_string (const char *str, char **string_const)
175 {
176   const char *start;
177   size_t len;
178 
179   str = sanei_config_skip_whitespace (str);
180 
181   if (*str == '"')
182     {
183       start = ++str;
184       while (*str && *str != '"')
185 	++str;
186       len = str - start;
187       if (*str == '"')
188 	++str;
189       else
190 	start = 0;		/* final double quote is missing */
191     }
192   else
193     {
194       start = str;
195       while (*str && !isspace (*str))
196 	++str;
197       len = str - start;
198     }
199   if (start)
200     *string_const = strndup (start, len);
201   else
202     *string_const = 0;
203   return str;
204 }
205 
206 char *
sanei_config_read(char * str,int n,FILE * stream)207 sanei_config_read (char *str, int n, FILE *stream)
208 {
209    char* rc;
210    char* start;
211    int   len;
212 
213       /* read line from stream */
214    rc = fgets( str, n, stream);
215    if (rc == NULL)
216       return NULL;
217 
218       /* remove ending whitespaces */
219    len = strlen( str);
220    while( (0 < len) && (isspace( str[--len])) )
221       str[len] = '\0';
222 
223       /* remove starting whitespaces */
224    start = str;
225    while( isspace( *start))
226       start++;
227 
228    if (start != str)
229       do {
230          *str++ = *start++;
231       } while( *str);
232 
233    return rc;
234 }
235 
236 
237 SANE_Status
sanei_configure_attach(const char * config_file,SANEI_Config * config,SANE_Status (* attach)(SANEI_Config * config,const char * devname,void * data),void * data)238 sanei_configure_attach (const char *config_file, SANEI_Config * config,
239 			SANE_Status (*attach) (SANEI_Config * config,
240 					       const char *devname, void *data),
241 			void *data)
242 {
243   SANE_Char line[PATH_MAX];
244   SANE_Char *token, *string;
245   SANE_Int len;
246   const char *lp, *lp2;
247   FILE *fp;
248   SANE_Status status = SANE_STATUS_GOOD;
249   int i, j, count;
250   void *value = NULL;
251   int size=0;
252   SANE_Bool found;
253   SANE_Word *wa;
254   SANE_Bool *ba;
255 
256   DBG (3, "sanei_configure_attach: start\n");
257 
258   /* open configuration file */
259   fp = sanei_config_open (config_file);
260   if (!fp)
261     {
262       DBG (2, "sanei_configure_attach: couldn't access %s\n", config_file);
263       DBG (3, "sanei_configure_attach: exit\n");
264       return SANE_STATUS_ACCESS_DENIED;
265     }
266 
267   /* loop reading the configuration file, all line beginning by "option " are
268    * parsed for value to store in configuration structure, other line are
269    * used are device to try to attach
270    */
271   while (sanei_config_read (line, PATH_MAX, fp) && status == SANE_STATUS_GOOD)
272     {
273       /* skip white spaces at beginning of line */
274       lp = sanei_config_skip_whitespace (line);
275 
276       /* skip empty lines */
277       if (*lp == 0)
278 	continue;
279 
280       /* skip comment line */
281       if (line[0] == '#')
282 	continue;
283 
284       len = strlen (line);
285 
286       /* delete newline characters at end */
287       if (line[len - 1] == '\n')
288 	line[--len] = '\0';
289 
290       lp2 = lp;
291 
292       /* to ensure maximum compatibility, we accept line like:
293        * option "option_name" "option_value"
294        * "option_name" "option_value"
295        * So we parse the line 2 time to find an option */
296       /* check if it is an option */
297       lp = sanei_config_get_string (lp, &token);
298       if (strncmp (token, "option", 6) == 0)
299 	{
300 	  /* skip the "option" token */
301 	  free (token);
302 	  lp = sanei_config_get_string (lp, &token);
303 	}
304 
305       /* search for a matching descriptor */
306       i = 0;
307       found = SANE_FALSE;
308       while (config!=NULL && i < config->count && !found)
309 	{
310 	  if (strcmp (config->descriptors[i]->name, token) == 0)
311 	    {
312 	      found = SANE_TRUE;
313 	      switch (config->descriptors[i]->type)
314 		{
315 		case SANE_TYPE_INT:
316 		  size=config->descriptors[i]->size;
317 		  value = malloc (size);
318 		  wa = (SANE_Word *) value;
319 		  count = config->descriptors[i]->size / sizeof (SANE_Word);
320 		  for (j = 0; j < count; j++)
321 		    {
322 		      lp = sanei_config_get_string (lp, &string);
323 		      if (string == NULL)
324 			{
325 			  DBG (2,
326 			       "sanei_configure_attach: couldn't find a string to parse");
327 			  return SANE_STATUS_INVAL;
328 			}
329 		      wa[j] = strtol (string, NULL, 0);
330 		      free (string);
331 		    }
332 		  break;
333 		case SANE_TYPE_BOOL:
334 		  size=config->descriptors[i]->size;
335 		  value = malloc (size);
336 		  ba = (SANE_Bool *) value;
337 		  count = config->descriptors[i]->size / sizeof (SANE_Bool);
338 		  for (j = 0; j < count; j++)
339 		    {
340 		      lp = sanei_config_get_string (lp, &string);
341 		      if (string == NULL)
342 			{
343 			  DBG (2,
344 			       "sanei_configure_attach: couldn't find a string to parse");
345 			  return SANE_STATUS_INVAL;
346 			}
347 		      if ((strcmp (string, "1") == 0)
348 			  || (strcmp (string, "true") == 0))
349 			{
350 			  ba[j] = SANE_TRUE;
351 			}
352 		      else
353 			{
354 			  if ((strcmp (string, "0") == 0)
355 			      || (strcmp (string, "false") == 0))
356 			    ba[j] = SANE_FALSE;
357 			  else
358 			    {
359 			      DBG (2,
360 				   "sanei_configure_attach: couldn't find a valid boolean value");
361 			      return SANE_STATUS_INVAL;
362 			    }
363 			}
364 		      free (string);
365 		    }
366 		  break;
367 		case SANE_TYPE_FIXED:
368 		  size=config->descriptors[i]->size;
369 		  value = malloc (size);
370 		  wa = (SANE_Word *) value;
371 		  count = config->descriptors[i]->size / sizeof (SANE_Word);
372 		  for (j = 0; j < count; j++)
373 		    {
374 		      lp = sanei_config_get_string (lp, &string);
375 		      if (string == NULL)
376 			{
377 			  DBG (2,
378 			       "sanei_configure_attach: couldn't find a string to parse");
379 			  return SANE_STATUS_INVAL;
380 			}
381 		      wa[j] = SANE_FIX(strtod (string, NULL));
382 		      free (string);
383 		    }
384 		  break;
385 		case SANE_TYPE_STRING:
386 		  sanei_config_get_string (lp, &string);
387 		  if (string == NULL)
388 		    {
389 		      DBG (2,
390 			   "sanei_configure_attach: couldn't find a string value to parse");
391 		      return SANE_STATUS_INVAL;
392 		    }
393 		  value = string;
394 		  size=strlen(string)+1;
395 		  if(size>config->descriptors[i]->size)
396 		  {
397 			  size=config->descriptors[i]->size-1;
398 			  string[size]=0;
399 		  }
400 		  break;
401 		default:
402 		  DBG (1,
403 		       "sanei_configure_attach: incorrect type %d for option %s, skipping option ...\n",
404 		       config->descriptors[i]->type,
405 		       config->descriptors[i]->name);
406 		}
407 
408 	      /* check decoded value */
409 	      status = sanei_check_value (config->descriptors[i], value);
410 
411 	      /* if value OK, copy it in configuration struct */
412 	      if (status == SANE_STATUS_GOOD)
413 		{
414 		  memcpy (config->values[i], value, size);
415 		}
416 	      if (value != NULL)
417 		{
418 		  free (value);
419 		  value = NULL;
420 		}
421 	    }
422 	  if (status != SANE_STATUS_GOOD)
423 	    {
424 	      DBG (1,
425 		   "sanei_configure_attach: failed to parse option '%s', line '%s'\n",
426 		   token, line);
427 	    }
428 	  i++;
429 	}
430       free (token);
431 
432       /* not detected as an option, so we call the attach function
433        * with it */
434       if (!found && status == SANE_STATUS_GOOD)
435 	{
436 	  /* if not an option, try to attach */
437 	  /* to avoid every backend to depend on scsi and usb functions
438 	   * we call back the backend for attach. In turn it will call
439 	   * sanei_usb_attach_matching_devices, sanei_config_attach_matching_devices
440 	   * or other. This means 2 callback functions per backend using this
441 	   * function. */
442 	  DBG (3, "sanei_configure_attach: trying to attach with '%s'\n",
443 	       lp2);
444 	  if(attach!=NULL)
445 	  	attach (config, lp2, data);
446 	}
447     }
448 
449   fclose (fp);
450   DBG (3, "sanei_configure_attach: exit\n");
451   return status;
452 }
453