1 /*
2 Copyright (c) 2000-2007 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 * llexec.c -- Frontend code for lifelines report generator
10 * Copyright(c) 2002-2007 by Perry Rapp; all rights reserved
11 *===========================================================*/
12
13 #include "llstdlib.h"
14 /* llstdlib.h pulls in standard.h, config.h, sys_inc.h */
15 #ifdef HAVE_LOCALE_H
16 #include <locale.h>
17 #endif
18 #include "table.h"
19 #include "translat.h"
20 #include "gedcom.h"
21 #include "liflines.h"
22 #include "arch.h"
23 #include "lloptions.h"
24 #include "interp.h"
25 #include "feedback.h"
26 #include "ui.h"
27 #include "llinesi.h"
28 #include "version.h"
29
30 #ifdef HAVE_GETOPT
31 #ifdef HAVE_GETOPT_H
32 #include <getopt.h>
33 #endif /* HAVE_GETOPT_H */
34 #endif /* HAVE_GETOPT */
35
36 /*********************************************
37 * external variables (no header)
38 *********************************************/
39
40 extern STRING qSmtitle,qSnorwandro,qSnofandl,qSbdlkar;
41 extern STRING qSusgFinnOpt,qSusgFinnAlw,qSusgNorm;
42 extern STRING qSbaddb,qSdefttl,qSiddefpath;
43 extern STRING qSaskynq,qSaskynyn,qSaskyY,qSaskint;
44 extern STRING qSchlistx,qSvwlistx;
45
46 extern INT csz_indi;
47 extern INT csz_fam;
48 extern INT csz_sour;
49 extern INT csz_even;
50 extern INT csz_othr;
51
52 extern int opterr;
53
54 /*********************************************
55 * required global variables
56 *********************************************/
57
58 static STRING usage_summary = ""; /* usage string */
59 int opt_finnish = FALSE; /* Finnish Language sorting order if TRUE */
60 int opt_mychar = FALSE; /* Custom character set handling (bypass libc) */
61 BOOLEAN debugmode = FALSE; /* no signal handling, so we can get coredump */
62 BOOLEAN opt_nocb = FALSE; /* no cb. data is displayed if TRUE */
63 BOOLEAN keyflag = TRUE; /* show key values */
64 BOOLEAN readonly = FALSE; /* database is read only */
65 BOOLEAN writeable = FALSE; /* database must be writeable */
66 BOOLEAN immutable = FALSE; /* make no changes at all to database, for access to truly read-only medium */
67 INT alldone = 0; /* completion flag */
68 BOOLEAN progrunning = FALSE; /* program is running */
69 BOOLEAN progparsing = FALSE; /* program is being parsed */
70 INT progerror = 0; /* error count during report program */
71 BOOLEAN traditional = TRUE; /* use traditional family rules */
72 BOOLEAN showusage = FALSE; /* show usage */
73 BOOLEAN showversion = FALSE; /* show version */
74 STRING readpath_file = NULL; /* last component of readpath */
75 STRING readpath = NULL; /* database path used to open */
76 STRING ext_codeset = 0; /* default codeset from locale */
77 INT screen_width = 20; /* TODO */
78
79 /*********************************************
80 * local function prototypes
81 *********************************************/
82
83 /* alphabetical */
84 static void print_usage(void);
85 static void load_usage(void);
86 static void main_db_notify(STRING db, BOOLEAN opening);
87 static void parse_arg(const char * optarg, char ** optname, char **optval);
88 static void platform_init(void);
89
90 /*********************************************
91 * local function definitions
92 * body of module
93 *********************************************/
94
95 /*==================================
96 * main -- Main routine of LifeLines
97 *================================*/
98 int
main(int argc,char ** argv)99 main (int argc, char **argv)
100 {
101 extern char *optarg;
102 extern int optind;
103 char * msg;
104 int c;
105 BOOLEAN ok=FALSE;
106 STRING dbrequested=NULL; /* database (path) requested */
107 STRING dbused=NULL; /* database (path) found */
108 BOOLEAN forceopen=FALSE, lockchange=FALSE;
109 char lockarg = 0; /* option passed for database lock */
110 INT alteration=0;
111 LIST exprogs=NULL;
112 TABLE exargs=NULL;
113 STRING progout=NULL;
114 BOOLEAN graphical=TRUE;
115 STRING configfile=0;
116 STRING crashlog=NULL;
117 int i=0;
118
119 /* initialize all the low-level library code */
120 init_stdlib();
121
122 #if HAVE_SETLOCALE
123 /* initialize locales */
124 setlocale(LC_ALL, "");
125 #endif /* HAVE_SETLOCALE */
126
127 /* capture user's default codeset */
128 ext_codeset = strsave(ll_langinfo());
129 /* TODO: We can use this info for default conversions */
130
131 #if ENABLE_NLS
132 /* setup gettext translation */
133 ll_bindtextdomain(PACKAGE, LOCALEDIR);
134 textdomain(PACKAGE);
135 #endif
136
137 save_original_locales();
138 load_usage();
139
140 /* handle conventional arguments --version and --help */
141 /* needed for help2man to synthesize manual pages */
142 for (i=1; i<argc; ++i) {
143 if (!strcmp(argv[i], "--version")
144 || !strcmp(argv[i], "-v")) {
145 print_version("llexec");
146 return 0;
147 }
148 if (!strcmp(argv[i], "--help")
149 || !strcmp(argv[i], "-h")
150 || !strcmp(argv[i], "-?")) {
151 print_usage();
152 return 0;
153 }
154 }
155
156 /* Parse Command-Line Arguments */
157 opterr = 0; /* turn off getopt's error message */
158 while ((c = getopt(argc, argv, "adkrwil:fntc:Fu:x:o:zC:I:vh?")) != -1) {
159 switch (c) {
160 case 'c': /* adjust cache sizes */
161 while(optarg && *optarg) {
162 if(isasciiletter((uchar)*optarg) && isupper((uchar)*optarg))
163 *optarg = tolower((uchar)*optarg);
164 if(*optarg == 'i') {
165 INT icsz_indi=0;
166 sscanf(optarg+1, "%ld,%ld", &csz_indi, &icsz_indi);
167 }
168 else if(*optarg == 'f') {
169 INT icsz_fam=0;
170 sscanf(optarg+1, "%ld,%ld", &csz_fam, &icsz_fam);
171 }
172 else if(*optarg == 's') {
173 INT icsz_sour=0;
174 sscanf(optarg+1, "%ld,%ld", &csz_sour, &icsz_sour);
175 }
176 else if(*optarg == 'e') {
177 INT icsz_even=0;
178 sscanf(optarg+1, "%ld,%ld", &csz_even, &icsz_even);
179 }
180 else if((*optarg == 'o') || (*optarg == 'x')) {
181 INT icsz_othr=0;
182 sscanf(optarg+1, "%ld,%ld", &csz_othr, &icsz_othr);
183 }
184 optarg++;
185 while(*optarg && isdigit((uchar)*optarg)) optarg++;
186 if(*optarg == ',') optarg++;
187 while(*optarg && isdigit((uchar)*optarg)) optarg++;
188 }
189 break;
190 #ifdef FINNISH
191 # ifdef FINNISHOPTION
192 case 'F': /* Finnish sorting order [toggle] */
193 opt_finnish = !opt_finnish;
194 /*
195 TO DO - need to mark Finnish databases, as
196 name records are not interoperable, because of
197 different soundex encoding
198 2001/02/17, Perry Rapp
199 */
200 break;
201 # endif
202 #endif
203 case 'a': /* debug allocation */
204 alloclog = TRUE;
205 break;
206 case 'd': /* debug = no signal catchers */
207 debugmode = TRUE;
208 break;
209 case 'k': /* don't show key values */
210 keyflag = FALSE;
211 break;
212 case 'r': /* request for read only access */
213 readonly = TRUE;
214 break;
215 case 'w': /* request for write access */
216 writeable = TRUE;
217 break;
218 case 'i': /* immutable access */
219 immutable = TRUE;
220 readonly = TRUE;
221 break;
222 case 'l': /* locking switch */
223 lockchange = TRUE;
224 lockarg = *optarg;
225 break;
226 case 'f': /* force database open in all cases */
227 forceopen = TRUE;
228 break;
229 case 'n': /* use non-traditional family rules */
230 traditional = FALSE;
231 break;
232 case 't': /* show lots of trace statements for debugging */
233 prog_trace = TRUE;
234 break;
235 case 'x': /* execute program */
236 if (!exprogs) {
237 exprogs = create_list2(LISTDOFREE);
238 }
239 push_list(exprogs, strdup(optarg ? optarg : ""));
240 break;
241 case 'I': /* program arguments */
242 {
243 STRING optname=0, optval=0;
244 parse_arg(optarg, &optname, &optval);
245 if (optname && optval) {
246 if (!exargs) {
247 exargs = create_table_str();
248 }
249 insert_table_str(exargs, optname, optval);
250 }
251 strfree(&optname);
252 strfree(&optval);
253 }
254 break;
255 case 'o': /* output directory */
256 progout = optarg;
257 break;
258 case 'z': /* nongraphical box */
259 graphical = FALSE;
260 break;
261 case 'C': /* specify config file */
262 configfile = optarg;
263 break;
264 case 'v': /* show version */
265 showversion = TRUE;
266 goto usage;
267 break;
268 case 'h': /* show usage */
269 case '?': /* show usage */
270 showversion = TRUE;
271 showusage = TRUE;
272 goto usage;
273 break;
274 }
275 }
276
277 prompt_for_db:
278
279 /* catch any fault, so we can close database */
280 if (!debugmode)
281 set_signals();
282 else /* developer wants to drive without seatbelt! */
283 stdstring_hardfail();
284
285 platform_init();
286 set_displaykeys(keyflag);
287 /* initialize options & misc. stuff */
288 llgettext_set_default_localedir(LOCALEDIR);
289 if (!init_lifelines_global(configfile, &msg, &main_db_notify)) {
290 llwprintf("%s", msg);
291 goto finish;
292 }
293 /* setup crashlog in case init_screen fails (eg, bad menu shortcuts) */
294 crashlog = getlloptstr("CrashLog_llexec", NULL);
295 if (!crashlog) { crashlog = "Crashlog_llexec.log"; }
296 crash_setcrashlog(crashlog);
297 init_interpreter(); /* give interpreter its turn at initialization */
298
299 /* Validate Command-Line Arguments */
300 if ((readonly || immutable) && writeable) {
301 llwprintf(_(qSnorwandro));
302 goto finish;
303 }
304 if (forceopen && lockchange) {
305 llwprintf(_(qSnofandl));
306 goto finish;
307 }
308 if (lockchange && lockarg != 'y' && lockarg != 'n') {
309 llwprintf(_(qSbdlkar));
310 goto finish;
311 }
312 if (forceopen)
313 alteration = 3;
314 else if (lockchange) {
315 if (lockarg == 'y')
316 alteration = 2;
317 else
318 alteration = 1;
319 }
320 c = argc - optind;
321 if (c > 1) {
322 showusage = TRUE;
323 goto usage;
324 }
325
326 /* Open database, prompting user if necessary */
327 if (1) {
328 STRING errmsg=0;
329 if (!alldone && c>0) {
330 dbrequested = strsave(argv[optind]);
331 } else {
332 strupdate(&dbrequested, "");
333 }
334 if (!select_database(dbrequested, alteration, &errmsg)) {
335 if (errmsg) {
336 llwprintf(errmsg);
337 }
338 alldone = 0;
339 goto finish;
340 }
341 }
342
343 /* Start Program */
344 if (!init_lifelines_postdb()) {
345 llwprintf(_(qSbaddb));
346 goto finish;
347 }
348 /* does not use show module */
349 /* does not use browse module */
350 if (exargs) {
351 set_cmd_options(exargs);
352 release_table(exargs);
353 exargs = 0;
354 }
355 if (exprogs) {
356 BOOLEAN picklist = FALSE;
357 BOOLEAN timing = FALSE;
358 interp_main(exprogs, progout, picklist, timing);
359 destroy_list(exprogs);
360 } else {
361 /* TODO: prompt for report filename */
362 }
363 /* does not use show module */
364 /* does not use browse module */
365 ok=TRUE;
366
367 finish:
368 /* we free this not because we care so much about these tiny amounts
369 of memory, but to ensure we have the memory management right */
370 /* strfree frees memory & nulls pointer */
371 strfree(&dbused);
372 strfree(&dbrequested);
373 strfree(&readpath_file);
374 shutdown_interpreter();
375 close_lifelines();
376 shutdown_ui(!ok);
377 if (alldone == 2)
378 goto prompt_for_db; /* changing databases */
379 termlocale();
380 strfree(&ext_codeset);
381
382 usage:
383 /* Display Version and/or Command-Line Usage Help */
384 if (showversion) { print_version("llexec"); }
385 if (showusage) puts(usage_summary);
386
387 /* Exit */
388 return !ok;
389 }
390 /*==================================
391 * parse_arg -- Break argument into name & value
392 * eg, parse_arg("main_indi=I3", &a, &b)
393 * yields a="main_indi" and b="I3"
394 * (a & b are newly allocated from heap)
395 *================================*/
396 static void
parse_arg(const char * optarg,char ** optname,char ** optval)397 parse_arg (const char * optarg, char ** optname, char **optval)
398 {
399 const char * ptr;
400 *optname = *optval = 0;
401 for (ptr = optarg; *ptr; ++ptr) {
402 if (*ptr == '=') {
403 char * namebuff = 0;
404 char * valbuff = 0;
405 INT namelen = ptr - optarg;
406 if (!namelen)
407 return;
408 namebuff = (char *)malloc(namelen+1);
409 llstrncpy(namebuff, optarg, namelen+1, 0);
410 *optname = namebuff;
411 valbuff = strdup(ptr+1);
412 *optval = valbuff;
413 return;
414
415 }
416 }
417 }
418 /*===================================================
419 * shutdown_ui -- (Placeholder, we don't need it)
420 *=================================================*/
421 void
shutdown_ui(BOOLEAN pause)422 shutdown_ui (BOOLEAN pause)
423 {
424 pause=pause; /* unused */
425 }
426 /*==================================================
427 * platform_init -- platform specific initialization
428 *================================================*/
429 static void
platform_init(void)430 platform_init (void)
431 {
432 /* TODO: We could do wtitle just like llines, but its declaration needs
433 to be moved somewhere more sensible for that (ie, not in curses.h!) */
434 }
435 /* Finnish language support modifies the soundex codes for names, so
436 * a database created with this support is not compatible with other
437 * databases.
438 *
439 * define FINNISH for Finnish Language support
440 *
441 * define FINNISHOPTION to have a runtime option -F which will enable
442 * Finnish language support, but the name indices will all be
443 * wrong if you make modifications whilst in the wrong mode.
444 */
445
446 static void
load_usage(void)447 load_usage (void)
448 {
449 #ifdef FINNISH
450 # ifdef FINNISHOPTION
451 opt_finnish = FALSE;/* Finnish Language sorting order if TRUE */
452 usage_summary = _(qSusgFinnOpt);
453 # else
454 opt_finnish = TRUE;/* Finnish Language sorting order if TRUE */
455 usage_summary = _(qSusgFinnAlw);
456 # endif
457 #else
458 opt_finnish = FALSE;/* Finnish Language sorting order id disabled*/
459 usage_summary = _(qSusgNorm);
460 #endif
461 }
462 /*===============================================
463 * print_usage -- display program help/usage
464 * displays to stdout
465 *=============================================*/
466 static void
print_usage(void)467 print_usage (void)
468 {
469 char * exename = "llexec";
470 print_lines_usage(exename);
471 }
472 /*==================================================
473 * main_db_notify -- callback called whenever a db is
474 * opened or closed
475 * Created: 2002/06/16, Perry Rapp
476 *================================================*/
477 static void
main_db_notify(STRING db,BOOLEAN opening)478 main_db_notify (STRING db, BOOLEAN opening)
479 {
480 /* store name away for reporting in case of crash later */
481 if (opening)
482 crash_setdb(db);
483 else
484 crash_setdb("");
485 }
486