• 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 (NULL == token)
299         {
300           // Invalid format?
301           continue;
302         }
303 
304       if (strncmp (token, "option", 6) == 0)
305 	{
306 	  /* skip the "option" token */
307 	  free (token);
308 	  lp = sanei_config_get_string (lp, &token);
309 	}
310 
311       /* search for a matching descriptor */
312       i = 0;
313       found = SANE_FALSE;
314       while (config!=NULL && i < config->count && !found)
315 	{
316 	  if (strcmp (config->descriptors[i]->name, token) == 0)
317 	    {
318 	      found = SANE_TRUE;
319 	      switch (config->descriptors[i]->type)
320 		{
321 		case SANE_TYPE_INT:
322 		  size=config->descriptors[i]->size;
323 		  value = malloc (size);
324 		  wa = (SANE_Word *) value;
325 		  count = config->descriptors[i]->size / sizeof (SANE_Word);
326 		  for (j = 0; j < count; j++)
327 		    {
328 		      lp = sanei_config_get_string (lp, &string);
329 		      if (string == NULL)
330 			{
331 			  DBG (2,
332 			       "sanei_configure_attach: couldn't find a string to parse");
333 			  return SANE_STATUS_INVAL;
334 			}
335 		      wa[j] = strtol (string, NULL, 0);
336 		      free (string);
337 		    }
338 		  break;
339 		case SANE_TYPE_BOOL:
340 		  size=config->descriptors[i]->size;
341 		  value = malloc (size);
342 		  ba = (SANE_Bool *) value;
343 		  count = config->descriptors[i]->size / sizeof (SANE_Bool);
344 		  for (j = 0; j < count; j++)
345 		    {
346 		      lp = sanei_config_get_string (lp, &string);
347 		      if (string == NULL)
348 			{
349 			  DBG (2,
350 			       "sanei_configure_attach: couldn't find a string to parse");
351 			  return SANE_STATUS_INVAL;
352 			}
353 		      if ((strcmp (string, "1") == 0)
354 			  || (strcmp (string, "true") == 0))
355 			{
356 			  ba[j] = SANE_TRUE;
357 			}
358 		      else
359 			{
360 			  if ((strcmp (string, "0") == 0)
361 			      || (strcmp (string, "false") == 0))
362 			    ba[j] = SANE_FALSE;
363 			  else
364 			    {
365 			      DBG (2,
366 				   "sanei_configure_attach: couldn't find a valid boolean value");
367 			      return SANE_STATUS_INVAL;
368 			    }
369 			}
370 		      free (string);
371 		    }
372 		  break;
373 		case SANE_TYPE_FIXED:
374 		  size=config->descriptors[i]->size;
375 		  value = malloc (size);
376 		  wa = (SANE_Word *) value;
377 		  count = config->descriptors[i]->size / sizeof (SANE_Word);
378 		  for (j = 0; j < count; j++)
379 		    {
380 		      lp = sanei_config_get_string (lp, &string);
381 		      if (string == NULL)
382 			{
383 			  DBG (2,
384 			       "sanei_configure_attach: couldn't find a string to parse");
385 			  return SANE_STATUS_INVAL;
386 			}
387 		      wa[j] = SANE_FIX(strtod (string, NULL));
388 		      free (string);
389 		    }
390 		  break;
391 		case SANE_TYPE_STRING:
392 		  sanei_config_get_string (lp, &string);
393 		  if (string == NULL)
394 		    {
395 		      DBG (2,
396 			   "sanei_configure_attach: couldn't find a string value to parse");
397 		      return SANE_STATUS_INVAL;
398 		    }
399 		  value = string;
400 		  size=strlen(string)+1;
401 		  if(size>config->descriptors[i]->size)
402 		  {
403 			  size=config->descriptors[i]->size-1;
404 			  string[size]=0;
405 		  }
406 		  break;
407 		default:
408 		  DBG (1,
409 		       "sanei_configure_attach: incorrect type %d for option %s, skipping option ...\n",
410 		       config->descriptors[i]->type,
411 		       config->descriptors[i]->name);
412 		}
413 
414 	      /* check decoded value */
415 	      status = sanei_check_value (config->descriptors[i], value);
416 
417 	      /* if value OK, copy it in configuration struct */
418 	      if (status == SANE_STATUS_GOOD)
419 		{
420 		  memcpy (config->values[i], value, size);
421 		}
422 	      if (value != NULL)
423 		{
424 		  free (value);
425 		  value = NULL;
426 		}
427 	    }
428 	  if (status != SANE_STATUS_GOOD)
429 	    {
430 	      DBG (1,
431 		   "sanei_configure_attach: failed to parse option '%s', line '%s'\n",
432 		   token, line);
433 	    }
434 	  i++;
435 	}
436       free (token);
437 
438       /* not detected as an option, so we call the attach function
439        * with it */
440       if (!found && status == SANE_STATUS_GOOD)
441 	{
442 	  /* if not an option, try to attach */
443 	  /* to avoid every backend to depend on scsi and usb functions
444 	   * we call back the backend for attach. In turn it will call
445 	   * sanei_usb_attach_matching_devices, sanei_config_attach_matching_devices
446 	   * or other. This means 2 callback functions per backend using this
447 	   * function. */
448 	  DBG (3, "sanei_configure_attach: trying to attach with '%s'\n",
449 	       lp2);
450 	  if(attach!=NULL)
451 	  	attach (config, lp2, data);
452 	}
453     }
454 
455   fclose (fp);
456   DBG (3, "sanei_configure_attach: exit\n");
457   return status;
458 }
459