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