1 /*
2 Copyright (c) 2000-2002 Perry Rapp
3 "The MIT license"
4 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8
9 /*==========================================================
10 * lloptions.c -- Read options from config file (& db user options)
11 * added in 3.0.6 by Perry Rapp
12 *========================================================*/
13
14 #ifdef HAVE_LOCALE_H
15 #include <locale.h>
16 #endif
17 #include "llstdlib.h"
18 #include "gedcom.h"
19 #include "gedcomi.h"
20 #include "lloptions.h"
21
22
23 /*********************************************
24 * external variables
25 *********************************************/
26
27 extern STRING qSopt2long;
28
29 /*********************************************
30 * local function prototypes
31 *********************************************/
32
33 /* alphabetical */
34 static void copy_process(STRING dest, STRING src);
35 static void expand_variables(STRING valbuf, INT max);
36 static INT load_config_file(STRING file, STRING * pmsg, STRING *chain);
37 static void send_notifications(void);
38
39 /*********************************************
40 * local variables
41 *********************************************/
42
43 /* table holding option values, both keys & values in heap */
44 /* listed in descending priority */
45 static TABLE f_cmd=0; /* option values from command line of current execution */
46 static TABLE f_rpt=0; /* option values local to currently running report program */
47 /* NB: cannot actually set any report option values currently 2002-10-07 */
48 static TABLE f_db=0; /* option values in current database (user/options) */
49 static TABLE f_global=0; /* option values from lines config file */
50 static TABLE f_predef=0; /* predefined variables during config file processing */
51 static TABLE f_fallback=0; /* lowest priority option values */
52 static LIST f_notifications=0; /* collection of callbacks for option table changes */
53
54 /*********************************************
55 * local function definitions
56 * body of module
57 *********************************************/
58
59 /*==========================================
60 * copy_process -- copy config value line,
61 * converting any escape characters
62 * This handles \n, \t, and \\
63 * We do not trim out backslashes, unless they
64 * are part of escape sequences. (This is mostly
65 * because backslashes are so prevalent in
66 * MS-Windows paths.)
67 * The output (dest) is no longer than the input (src).
68 * Created: 2001/11/09, Perry Rapp
69 *========================================*/
70 static void
copy_process(STRING dest,STRING src)71 copy_process (STRING dest, STRING src)
72 {
73 STRING q=dest,p=src;
74 while ((*q = *p++)) {
75 if (*q == '\\') {
76 switch (*p++) {
77 case 0:
78 *++q = 0;
79 break;
80 case 'n':
81 *q = '\n';
82 break;
83 case 't':
84 *q = '\t';
85 break;
86 case '\\':
87 *q = '\\';
88 break;
89 default:
90 --p;
91 }
92 }
93 ++q;
94 }
95 }
96 /*==========================================
97 * expand_variables -- do any variable substitutions
98 * (variables are option properties starting & ending with %
99 * Created: 2002/10/21, Perry Rapp
100 *========================================*/
101 static void
expand_variables(STRING valbuf,INT max)102 expand_variables (STRING valbuf, INT max)
103 {
104 STRING start, end;
105 STRING ptr; /* remainder of valbuf to check */
106 ptr = valbuf;
107 while ((start=strchr(ptr, '%')) && (end=strchr(start+1, '%'))) {
108 STRING name = allocsubbytes(start, 0, end-start+1);
109 STRING value = valueof_str(f_global, name);
110 if (!value)
111 value = valueof_str(f_predef, name);
112 if (value) {
113 INT newlen = strlen(valbuf)-(end-start+1)+strlen(value);
114 if (newlen < max) {
115 STRING copy = strdup(valbuf);
116 if (start>valbuf)
117 strncpy(valbuf, copy, start-valbuf);
118 strcpy(start, value);
119 strcpy(start+strlen(value), copy+(end-valbuf+1));
120 stdfree(copy);
121 }
122 }
123 stdfree(name);
124 ptr = end+1;
125 }
126 }
127 /*==========================================
128 * dir_from_file -- return directory of file
129 * heap-allocated
130 *========================================*/
131 static STRING
dir_from_file(STRING file)132 dir_from_file (STRING file)
133 {
134 STRING thisdir = strdup(file);
135 STRING ptr;
136 for (ptr=thisdir+strlen(thisdir)-1; ptr>thisdir; --ptr) {
137 if (is_dir_sep(*ptr))
138 break;
139 }
140 *ptr = 0;
141 return thisdir;
142 }
143 /*==========================================
144 * load_config_file -- read options in config file
145 * and load into table (f_global)
146 * returns 1 for success, 0 for not found, -1 for error (with pmsg)
147 *========================================*/
148 static INT
load_config_file(STRING file,STRING * pmsg,STRING * chain)149 load_config_file (STRING file, STRING * pmsg, STRING *chain)
150 {
151 FILE * fp = 0;
152 STRING ptr, val, key;
153 STRING thisdir = dir_from_file(file);
154 BOOLEAN failed, noesc;
155 char buffer[MAXLINELEN],valbuf[MAXLINELEN];
156 INT len;
157 fp = fopen(file, LLREADTEXT);
158 if (!fp) {
159 free(thisdir);
160 return 0; /* 0 for not found */
161 }
162 f_predef = create_table_str();
163
164 insert_table_str(f_predef, "%thisdir%", thisdir);
165 strfree(&thisdir);
166 /* read thru config file til done (or error) */
167 while (fgets(buffer, sizeof(buffer), fp)) {
168 noesc = FALSE;
169 len = strlen(buffer);
170 if (len == 0)
171 continue; /* ignore blank lines */
172 if (buffer[0] == '#')
173 continue; /* ignore lines starting with # */
174 if (!feof(fp) && buffer[len-1] != '\n') {
175 /* bail out if line too long */
176 break;
177 }
178 chomp(buffer); /* trim any trailing CR or LF */
179 /* find =, which separates key from value */
180 for (ptr = buffer; *ptr && *ptr!='='; ptr++)
181 ;
182 if (*ptr != '=' || ptr==buffer)
183 continue; /* ignore lines without = or key */
184 *ptr=0; /* zero-terminate key */
185 if (ptr[-1] == ':') {
186 noesc = TRUE; /* := means don't do backslash escapes */
187 ptr[-1] = 0;
188 }
189 /* ignore any previous value, it will be overwritten */
190 /* advance over separator to value */
191 ptr++;
192 /*
193 process the value into valbuf
194 this handles escapes (eg, "\n")
195 the output (valbuf) is no longer than the input (ptr)
196 */
197 if (noesc)
198 llstrncpy(valbuf, ptr, sizeof(valbuf), uu8);
199 else
200 copy_process(valbuf, ptr);
201 expand_variables(valbuf, sizeof(valbuf));
202 key = buffer; /* key is in beginning of buffer, we zero-terminated it */
203 val = valbuf;
204 if (strcmp(key,"LLCONFIGFILE") == 0) {
205 /* LLCONFIGFILE is not entered in table, only used to
206 * chain to another config file
207 */
208 *chain = strsave(val);
209 } else {
210 insert_table_str(f_global, key, val);
211 }
212 }
213 failed = !feof(fp);
214 fclose(fp);
215 if (failed) {
216 /* error is in heap */
217 *pmsg = strsave(_(qSopt2long));
218 return -1; /* -1 for error */
219 }
220 free_optable(&f_predef);
221 send_notifications();
222 return 1; /* 1 for ok */
223 }
224 /*=================================
225 * load_global_options --
226 * Load internal table of global options from caller-specified config file
227 * STRING * pmsg: heap-alloc'd error string if fails
228 * returns 1 for ok, 0 for not found, -1 for error
229 *===============================*/
230 INT
load_global_options(STRING configfile,STRING * pmsg)231 load_global_options (STRING configfile, STRING * pmsg)
232 {
233 STRING chain = NULL;
234 INT rtn = 0;
235 INT cnt = 0;
236 *pmsg = NULL;
237 if (!f_global)
238 f_global= create_table_str();
239 do {
240 if (chain) strfree(&chain);
241 rtn = load_config_file(configfile, pmsg, &chain);
242 if (rtn == -1) {
243 if (chain) strfree(&chain);
244 return rtn;
245 }
246 if (++cnt > 100) {
247 return -1; /* prevent infinite recursion */
248 }
249 } while (chain);
250
251 return rtn;
252 }
253 /*=================================
254 * set_cmd_options -- Store cmdline options from caller
255 *===============================*/
256 void
set_cmd_options(TABLE opts)257 set_cmd_options (TABLE opts)
258 {
259 ASSERT(opts);
260 release_table(f_cmd);
261 f_cmd = opts;
262 addref_table(f_cmd);
263 send_notifications();
264 }
265 /*=================================
266 * set_db_options -- Store db options from caller
267 * Created: 2002/06/16, Perry Rapp
268 *===============================*/
269 void
set_db_options(TABLE opts)270 set_db_options (TABLE opts)
271 {
272 ASSERT(opts);
273 release_table(f_db);
274 f_db = opts;
275 addref_table(f_db);
276 send_notifications();
277 }
278 /*=================================
279 * get_db_options -- Copy db options to caller's table
280 * Created: 2002/06/16, Perry Rapp
281 *===============================*/
282 void
get_db_options(TABLE opts)283 get_db_options (TABLE opts)
284 {
285 if (!f_db)
286 f_db = create_table_str();
287 copy_table(f_db, opts);
288 }
289 /*==========================================
290 * free_optable -- free a table if it exists
291 *========================================*/
292 void
free_optable(TABLE * ptab)293 free_optable (TABLE * ptab)
294 {
295 if (*ptab) {
296 release_table(*ptab);
297 *ptab = 0;
298 }
299 }
300 /*==========================================
301 * term_lloptions -- deallocate structures
302 * used by lloptions at program termination
303 * Safe to be called more than once
304 * Created: 2001/04/30, Matt Emmerton
305 *========================================*/
306 void
term_lloptions(void)307 term_lloptions (void)
308 {
309 free_optable(&f_cmd);
310 free_optable(&f_rpt);
311 free_optable(&f_db);
312 free_optable(&f_global);
313 free_optable(&f_fallback);
314 remove_listeners(&f_notifications);
315 }
316 /*===============================================
317 * getlloptstr -- get an option (from db or from global)
318 * Example:
319 * str = getlloptstr("HDR_SUBM", "1 SUBM");
320 * returns string belonging to table
321 *=============================================*/
322 STRING
getlloptstr(CNSTRING optname,STRING defval)323 getlloptstr (CNSTRING optname, STRING defval)
324 {
325 STRING str = 0;
326 if (!str && f_cmd)
327 str = valueof_str(f_cmd, optname);
328 if (!str && f_db)
329 str = valueof_str(f_db, optname);
330 if (!str && f_global)
331 str = valueof_str(f_global, optname);
332 if (!str && f_fallback)
333 str = valueof_str(f_fallback, optname);
334 if (!str)
335 str = defval;
336 return str;
337 }
338 /*===============================================
339 * getlloptstr_rpt -- get an option (checking report-local options first)
340 * Example:
341 * str = getlloptstr_rpt("HDR_SUBM", "1 SUBM");
342 * Created: 2002/06/16, Perry Rapp
343 *=============================================*/
344 STRING
getlloptstr_rpt(CNSTRING optname,STRING defval)345 getlloptstr_rpt (CNSTRING optname, STRING defval)
346 {
347 STRING str = 0;
348 if (!str && f_rpt)
349 str = valueof_str(f_rpt, optname);
350 if (!str)
351 str = getlloptstr(optname, defval);
352 return str;
353 }
354 /*===============================================
355 * getlloptstr_dbonly -- get an option (but only look at db options)
356 * Example:
357 * str = getlloptstr_dbonly("codeset", 0);
358 * Created: 2002/06/16, Perry Rapp
359 *=============================================*/
360 STRING
getlloptstr_dbonly(CNSTRING optname,STRING defval)361 getlloptstr_dbonly (CNSTRING optname, STRING defval)
362 {
363 STRING str = 0;
364 if (f_db)
365 str = valueof_str(f_db, optname);
366 if (!str)
367 str = defval;
368 return str;
369 }
370 /*===============================================
371 * getlloptint -- get a numerical option
372 * First tries user option table (looks up optname)
373 * Then tries config option table
374 * Finally defaults to defval
375 * Example:
376 if (getlloptint("FullReportCallStack", 0) > 0)
377 * Created: 2001/11/22, Perry Rapp
378 *=============================================*/
379 INT
getlloptint(CNSTRING optname,INT defval)380 getlloptint (CNSTRING optname, INT defval)
381 {
382 STRING str = getlloptstr(optname, 0);
383 return str ? atoi(str) : defval;
384 }
385 /*===============================================
386 * setoptstr_fallback -- Set option fallback value
387 *=============================================*/
388 void
setoptstr_fallback(STRING optname,STRING newval)389 setoptstr_fallback (STRING optname, STRING newval)
390 {
391 if (!f_fallback)
392 f_fallback = create_table_str();
393 replace_table_str(f_fallback, optname, newval);
394 send_notifications();
395 }
396 /*===============================================
397 * register_notify -- Caller wants to be notified when options change
398 *=============================================*/
399 void
register_notify(CALLBACK_FNC fncptr)400 register_notify (CALLBACK_FNC fncptr)
401 {
402 add_listener(&f_notifications, fncptr, 0);
403 }
404 /*===============================================
405 * unregister_notify -- Caller no longer wants to be notified when options change
406 *=============================================*/
407 void
unregister_notify(CALLBACK_FNC fncptr)408 unregister_notify (CALLBACK_FNC fncptr)
409 {
410 delete_listener(&f_notifications, fncptr, 0);
411 }
412 /*===============================================
413 * send_notifications -- Send notifications to any registered listeners
414 *=============================================*/
415 static void
send_notifications(void)416 send_notifications (void)
417 {
418 notify_listeners(&f_notifications);
419 }
420