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