1 /* GNUPLOT - plot.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 #include "syscfg.h"
34 #include "plot.h"
35 
36 #include "alloc.h"
37 #include "command.h"
38 #include "eval.h"
39 #include "fit.h"
40 #include "gp_hist.h"
41 #include "misc.h"
42 #include "readline.h"
43 #include "setshow.h"
44 #include "term_api.h"
45 #include "util.h"
46 #include "variable.h"
47 #include "version.h"
48 #include "voxelgrid.h"
49 #include "encoding.h"
50 
51 #include <signal.h>
52 #include <setjmp.h>
53 
54 #ifdef OS2 /* os2.h required for gpexecute.h */
55 # define INCL_DOS
56 # define INCL_REXXSAA
57 # ifdef OS2_IPC
58 #  define INCL_DOSSEMAPHORES
59 # endif
60 # include <os2.h>
61 #endif /* OS2 */
62 
63 /* on OS/2 this is needed even without USE_MOUSE */
64 #if defined(OS2_IPC)
65 # include "gpexecute.h"
66 #endif
67 
68 #if defined(MSDOS) || defined(__EMX__) || (defined(WGP_CONSOLE) && defined(MSVC))
69 # include <io.h>
70 #endif
71 
72 #ifdef VMS
73 # ifndef __GNUC__
74 #  include <unixio.h>
75 # endif
76 # include <smgdef.h>
77 # include <ssdef.h>
78 extern int vms_vkid;
79 extern smg$create_virtual_keyboard();
80 extern int vms_ktid;
81 extern smg$create_key_table();
82 #endif /* VMS */
83 
84 #ifdef _WIN32
85 # include <windows.h>
86 # include "win/winmain.h"
87 # include "win/wcommon.h"
88 # include <io.h>           // for isatty
89 #endif /* _WIN32 */
90 
91 /* GNU readline
92  * Only required by two files directly,
93  * so I don't put this into a header file. -lh
94  */
95 #if defined(HAVE_LIBREADLINE) && !defined(MISSING_RL_TILDE_EXPANSION)
96 #  include <readline/tilde.h>
97    extern int rl_complete_with_tilde_expansion;
98 #endif
99 
100 /* BSD editline
101 */
102 #ifdef HAVE_LIBEDITLINE
103 # include <editline/readline.h>
104 #endif
105 
106 /* enable gnuplot history with readline */
107 #ifdef GNUPLOT_HISTORY
108 # ifndef GNUPLOT_HISTORY_FILE
109 #  define GNUPLOT_HISTORY_FILE "~/.gnuplot_history"
110 # endif
111 /*
112  * expanded_history_filename points to the value from 'tilde_expand()',
113  * which expands '~' to the user's home directory, or $HOME.
114  * Depending on your OS you have to make sure that the "$HOME" environment
115  * variable exists.  You are responsible for valid values.
116  */
117 static char *expanded_history_filename;
118 
119 static void wrapper_for_write_history(void);
120 
121 #endif				/* GNUPLOT_HISTORY */
122 
123 TBOOLEAN interactive = TRUE;	/* FALSE if stdin not a terminal */
124 TBOOLEAN noinputfiles = TRUE;	/* FALSE if there are script files */
125 TBOOLEAN reading_from_dash=FALSE;	/* True if processing "-" as an input file */
126 TBOOLEAN skip_gnuplotrc = FALSE;	/* skip system gnuplotrc and ~/.gnuplot */
127 TBOOLEAN persist_cl = FALSE; 		/* --persist command line option */
128 TBOOLEAN slow_font_startup = FALSE;	/* --slow command line option */
129 
130 /* user home directory */
131 static const char *user_homedir = NULL;
132 
133 /* user shell */
134 const char *user_shell = NULL;
135 
136 /* not static because unset.c refers to it when in debugging mode */
137 TBOOLEAN successful_initialization = FALSE;
138 
139 #ifdef X11
140 extern int X11_args(int, char **); /* FIXME: defined in term/x11.trm */
141 #endif
142 
143 /* patch to get home dir, see command.c */
144 #if defined(MSDOS) || defined(OS2)
145 char HelpFile[PATH_MAX];
146 static char progpath[PATH_MAX] = "";
147 #endif
148 
149 /* a longjmp buffer to get back to the command line */
150 static JMP_BUF command_line_env;
151 
152 static void load_rcfile(int where);
153 static RETSIGTYPE inter(int anint);
154 static void init_memory(void);
155 
156 static int exit_status = EXIT_SUCCESS;
157 
158 /* Flag for asynchronous handling of Ctrl-C. Used by fit.c and Windows */
159 TBOOLEAN ctrlc_flag = FALSE;
160 /* Flag for (asynchronous) term signal on Windows. */
161 TBOOLEAN terminate_flag = FALSE;
162 
163 #ifdef OS2
164 # include <process.h>
165 static ULONG RexxInterface(PRXSTRING, PUSHORT, PRXSTRING);
166 TBOOLEAN CallFromRexx = FALSE;
167 #endif /* OS2 */
168 
169 static RETSIGTYPE
inter(int anint)170 inter(int anint)
171 {
172     (void) anint;		/* avoid -Wunused warning */
173     (void) signal(SIGINT, (sigfunc) inter);
174     (void) signal(SIGFPE, SIG_DFL);	/* turn off FPE trapping */
175 
176 #ifdef OS2
177     if (!strcmp(term->name,"pm")) {
178 	PM_intc_cleanup();
179 	/* ??
180 	  putc('\n', stderr);
181 	  LONGJMP(command_line_env, TRUE);
182 	 */
183     } else
184 #endif
185 #if defined(WGP_CONSOLE)
186 	/* The Windows console Ctrl-C handler runs in another thread. So a
187 	   longjmp() would result in crash. Instead, we handle these
188 	   events asynchronously.
189 	*/
190 	ctrlc_flag = TRUE;
191 	/* Interrupt ConsoleGetch. */
192 	SendMessage(graphwin->hWndGraph, WM_NULL, 0, 0);
193 	SendMessage(GetConsoleWindow(), WM_CHAR, 0x20, 0);
194 #else
195     {
196     term_reset();
197     (void) putc('\n', stderr);
198     bail_to_command_line();	/* return to prompt */
199     }
200 #endif
201 }
202 
203 #ifdef LINUXVGA
204 /* utility functions to ensure that setuid gnuplot
205  * assumes root privileges only for those parts
206  * of the code which require root rights.
207  *
208  * By "Dr. Werner Fink" <werner@suse.de>
209  */
210 static uid_t euid, ruid;
211 static gid_t egid, rgid;
212 static int asked_privi = 0;
213 
214 void
drop_privilege()215 drop_privilege()
216 {
217     if (!asked_privi) {
218 	euid = geteuid();
219 	egid = getegid();
220 	ruid = getuid();
221 	rgid = getgid();
222 	asked_privi = 1;
223     }
224     if (setegid(rgid) == -1)
225 	(void) fprintf(stderr, "setegid(%d): %s\n",
226 		       (int) rgid, strerror(errno));
227     if (seteuid(ruid) == -1)
228 	(void) fprintf(stderr, "seteuid(%d): %s\n",
229 		       (int) ruid, strerror(errno));
230 }
231 
232 void
take_privilege()233 take_privilege()
234 {
235     if (!asked_privi) {
236 	euid = geteuid();
237 	egid = getegid();
238 	ruid = getuid();
239 	rgid = getgid();
240 	asked_privi = 1;
241     }
242     if (setegid(egid) == -1)
243 	(void) fprintf(stderr, "setegid(%d): %s\n",
244 		       (int) egid, strerror(errno));
245     if (seteuid(euid) == -1)
246 	(void) fprintf(stderr, "seteuid(%d): %s\n",
247 		       (int) euid, strerror(errno));
248 }
249 
250 #endif /* LINUXVGA */
251 
252 /* a wrapper for longjmp so we can keep everything local */
253 void
bail_to_command_line()254 bail_to_command_line()
255 {
256 #ifdef _WIN32
257     kill_pending_Pause_dialog();
258     ctrlc_flag = FALSE;
259 #endif
260     LONGJMP(command_line_env, TRUE);
261 }
262 
263 #if defined(_WIN32)
264 int
gnu_main(int argc_orig,char ** argv)265 gnu_main(int argc_orig, char **argv)
266 #else
267 int
268 main(int argc_orig, char **argv)
269 #endif
270 {
271     int i;
272 
273     /* We want the current value of argc to persist across a LONGJMP from int_error().
274      * Without this the compiler may put it on the stack, which LONGJMP clobbers.
275      * Here we try make it a volatile variable that optimization will not affect.
276      * Why do we not have to do the same for argv?   I don't know.
277      * But the test cases that broke with generic argc seem fine with generic argv.
278      */
279     static volatile int argc;
280     argc = argc_orig;
281 
282 #ifdef LINUXVGA
283     LINUX_setup();		/* setup VGA before dropping privilege DBT 4/5/99 */
284     drop_privilege();
285 #endif
286 /* make sure that we really have revoked root access, this might happen if
287    gnuplot is compiled without vga support but is installed suid by mistake */
288 #ifdef __linux__
289     if (setuid(getuid()) != 0) {
290 	fprintf(stderr,"gnuplot: refusing to run at elevated privilege\n");
291 	exit(EXIT_FAILURE);
292     }
293 #endif
294 
295 /* HBB: Seems this isn't needed any more for DJGPP V2? */
296 /* HBB: disable all floating point exceptions, just keep running... */
297 #if defined(DJGPP) && (DJGPP!=2)
298     _control87(MCW_EM, MCW_EM);
299 #endif
300 
301 #if defined(OS2)
302     {
303 	int rc;
304 #ifdef OS2_IPC
305 	char semInputReadyName[40];
306 
307 	sprintf(semInputReadyName, "\\SEM32\\GP%i_Input_Ready", getpid());
308 	rc = DosCreateEventSem(semInputReadyName, &semInputReady, 0, 0);
309 	if (rc != 0)
310 	    fputs("DosCreateEventSem error\n", stderr);
311 #endif
312 	rc = RexxRegisterSubcomExe("GNUPLOT", (PFN) RexxInterface, NULL);
313     }
314 #endif
315 
316 /* malloc large blocks, otherwise problems with fragmented mem */
317 #ifdef MALLOCDEBUG
318     malloc_debug(7);
319 #endif
320 
321 
322 /* init progpath and get helpfile from executable directory */
323 #if defined(MSDOS) || defined(OS2)
324     {
325 	char *s;
326 
327 #ifdef __EMX__
328 	_execname(progpath, sizeof(progpath));
329 #else
330 	safe_strncpy(progpath, argv[0], sizeof(progpath));
331 #endif
332 	/* convert '/' to '\\' */
333 	for (s = progpath; *s != NUL; s++)
334 	    if (*s == DIRSEP2)
335 		*s = DIRSEP1;
336 	/* cut program name */
337 	s = strrchr(progpath, DIRSEP1);
338 	if (s != NULL)
339 	    s++;
340 	else
341 	    s = progpath;
342 	*s = NUL;
343 	/* init HelpFile */
344 	strcpy(HelpFile, progpath);
345 	strcat(HelpFile, "gnuplot.gih");
346 	/* remove trailing "bin/" from progpath */
347 	if ((s != NULL) && (s - progpath >= 4)) {
348 	    s -= 4;
349 	    if (strncasecmp(s, "bin", 3) == 0)
350 		*s = NUL;
351 	}
352     }
353 #endif /* DJGPP */
354 
355 #if (defined(PIPE_IPC) || defined(_WIN32)) && (defined(HAVE_LIBREADLINE) || (defined(HAVE_LIBEDITLINE) && defined(X11)))
356     /* Editline needs this to be set before the very first call to readline(). */
357     /* Support for rl_getc_function is broken for utf-8 in editline. Since it is only
358        really required for X11, disable this section when building without X11. */
359     rl_getc_function = getc_wrapper;
360 #endif
361 
362 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
363     /* T.Walter 1999-06-24: 'rl_readline_name' must be this fix name.
364      * It is used to parse a 'gnuplot' specific section in '~/.inputrc'
365      * or gnuplot specific commands in '.editrc' (when using editline
366      * instead of readline) */
367     rl_readline_name = "Gnuplot";
368     rl_terminal_name = getenv("TERM");
369 #if defined(HAVE_LIBREADLINE)
370     using_history();
371 #else
372     history_init();
373 #endif
374 #endif
375 #if defined(HAVE_LIBREADLINE) && !defined(MISSING_RL_TILDE_EXPANSION)
376     rl_complete_with_tilde_expansion = 1;
377 #endif
378 
379     for (i = 1; i < argc; i++) {
380 	if (!argv[i])
381 	    continue;
382 
383 	if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) {
384 	    printf("gnuplot %s patchlevel %s\n",
385 		    gnuplot_version, gnuplot_patchlevel);
386 	    return 0;
387 
388 	} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
389 	    printf( "Usage: gnuplot [OPTION] ... [FILE]\n"
390 #ifdef X11
391 		    "for X11 options see 'help X11->command-line-options'\n"
392 #endif
393 		    "  -V, --version\n"
394 		    "  -h, --help\n"
395 		    "  -p  --persist\n"
396 		    "  -s  --slow\n"
397 		    "  -d  --default-settings\n"
398 		    "  -c  scriptfile ARG1 ARG2 ... \n"
399 		    "  -e  \"command1; command2; ...\"\n"
400 		    "gnuplot %s patchlevel %s\n",
401 		    gnuplot_version, gnuplot_patchlevel);
402 #ifdef DEVELOPMENT_VERSION
403 	    printf(
404 #ifdef DIST_CONTACT
405 		    "Report bugs to "DIST_CONTACT"\n"
406 		    "            or %s\n",
407 #else
408 		    "Report bugs to %s\n",
409 #endif
410 		    bug_email);
411 #endif
412 	    return 0;
413 
414 	} else if (!strncmp(argv[i], "-persist", 2) || !strcmp(argv[i], "--persist")
415 #ifdef _WIN32
416 		|| !stricmp(argv[i], "-noend") || !stricmp(argv[i], "/noend")
417 #endif
418 		) {
419 	    persist_cl = TRUE;
420 	} else if (!strncmp(argv[i], "-slow", 2) || !strcmp(argv[i], "--slow")) {
421 	    slow_font_startup = TRUE;
422 	} else if (!strncmp(argv[i], "-d", 2) || !strcmp(argv[i], "--default-settings")) {
423 	    /* Skip local customization read from ~/.gnuplot */
424 	    skip_gnuplotrc = TRUE;
425 	}
426     }
427 
428 #ifdef X11
429     /* the X11 terminal removes tokens that it recognizes from argv. */
430     {
431 	int n = X11_args(argc, argv);
432 	argv += n;
433 	argc -= n;
434     }
435 #endif
436 
437     setbuf(stderr, (char *) NULL);
438 
439 #ifdef HAVE_SETVBUF
440     /* This was once setlinebuf(). Docs say this is
441      * identical to setvbuf(,NULL,_IOLBF,0), but MS C
442      * faults this (size out of range), so we try with
443      * size of 1024 instead. [SAS/C does that, too. -lh]
444      */
445     if (setvbuf(stdout, (char *) NULL, _IOLBF, (size_t) 1024) != 0)
446 	(void) fputs("Could not linebuffer stdout\n", stderr);
447 
448     /* Switching to unbuffered mode causes all characters in the input
449      * buffer to be lost. So the only safe time to do it is on program entry.
450      * Do any non-X platforms suffer from this problem?
451      * EAM - Jan 2013 YES.
452      */
453     setvbuf(stdin, (char *) NULL, _IONBF, 0);
454 #endif
455 
456     gpoutfile = stdout;
457 
458     /* Initialize pre-loaded user variables */
459     /* "pi" is hard-wired as the first variable */
460     (void) add_udv_by_name("GNUTERM");
461     (void) add_udv_by_name("NaN");
462     init_constants();
463     udv_user_head = &(udv_NaN->next_udv);
464 
465     init_memory();
466 
467     interactive = FALSE;
468 
469     /* April 2017:  We used to call init_terminal() here, but now   */
470     /* We defer initialization until error handling has been set up. */
471 
472 # if defined(_WIN32) && !defined(WGP_CONSOLE)
473     interactive = TRUE;
474 # else
475     interactive = isatty(fileno(stdin));
476 # endif
477 
478     /* Note: we want to know whether this is an interactive session so that we can
479      * decide whether or not to write status information to stderr.  The old test
480      * for this was to see if (argc > 1) but the addition of optional command line
481      * switches broke this.  What we really wanted to know was whether any of the
482      * command line arguments are file names or an explicit in-line "-e command".
483      */
484     for (i = 1; i < argc; i++) {
485 # ifdef _WIN32
486 	if (!stricmp(argv[i], "/noend"))
487 	    continue;
488 # endif
489 	if ((argv[i][0] != '-') || (argv[i][1] == 'e') || (argv[i][1] == 'c') ) {
490 	    interactive = FALSE;
491 	    break;
492 	}
493     }
494 
495     /* Need this before show_version is called for the first time */
496 
497     if (interactive)
498 	show_version(stderr);
499     else
500 	show_version(NULL); /* Only load GPVAL_COMPILE_OPTIONS */
501 
502     update_gpval_variables(3);  /* update GPVAL_ variables available to user */
503 
504 #ifdef VMS
505     /* initialise screen management routines for command recall */
506     {
507     unsigned int ierror;
508     if (ierror = smg$create_virtual_keyboard(&vms_vkid) != SS$_NORMAL)
509 	done(ierror);
510     if (ierror = smg$create_key_table(&vms_ktid) != SS$_NORMAL)
511 	done(ierror);
512     }
513 #endif /* VMS */
514 
515     if (!SETJMP(command_line_env, 1)) {
516 	/* first time */
517 	interrupt_setup();
518 	get_user_env();
519 	init_loadpath();
520 	init_locale();
521 
522 	memset(&sm_palette, 0, sizeof(sm_palette));
523 	init_fit();		/* Initialization of fitting module */
524 #ifdef READLINE
525 	/* When using the built-in readline, we set the initial
526 	   encoding according to the locale as this is required
527 	   to properly handle keyboard input. */
528 	init_encoding();
529 #endif
530 	init_gadgets();
531 
532 	/* April 2017: Now that error handling is in place, it is safe parse
533 	 * GNUTERM during terminal initialization.
534 	 * atexit processing is done in reverse order. We want
535 	 * the generic terminal shutdown in term_reset to be executed before
536 	 * any terminal specific cleanup requested by individual terminals.
537 	 */
538 	init_terminal();
539 	push_terminal(0);	/* remember the initial terminal */
540 	gp_atexit(term_reset);
541 
542 	/* Execute commands in ~/.gnuplot */
543 	init_session();
544 
545 	if (interactive && term != 0) {		/* not unknown */
546 #ifdef GNUPLOT_HISTORY
547 #if (defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)) && !defined(_WIN32)
548 	    expanded_history_filename = tilde_expand(GNUPLOT_HISTORY_FILE);
549 #else
550 	    expanded_history_filename = gp_strdup(GNUPLOT_HISTORY_FILE);
551 	    gp_expand_tilde(&expanded_history_filename);
552 #endif
553 	    read_history(expanded_history_filename);
554 
555 	    /*
556 	     * It is safe to ignore the return values of 'atexit()' and
557 	     * 'on_exit()'. In the worst case, there is no history of your
558 	     * current session and you have to type all again in your next
559 	     * session.
560 	     */
561 	    gp_atexit(wrapper_for_write_history);
562 #endif /* GNUPLOT_HISTORY */
563 
564 #if defined(READLINE) && defined(WGP_CONSOLE)
565 	    fprintf(stderr, "Encoding set to '%s'.\n", encoding_names[encoding]);
566 #endif
567 	}			/* if (interactive && term != 0) */
568     } else {
569 	/* come back here from int_error() */
570 	if (!successful_initialization) {
571 	    /* Only print the warning once */
572 	    successful_initialization = TRUE;
573 	    fprintf(stderr,"WARNING: Error during initialization\n\n");
574 	}
575 	if (interactive == FALSE)
576 	    exit_status = EXIT_FAILURE;
577 #ifdef HAVE_READLINE_RESET
578 	else {
579 	    /* reset properly readline after a SIGINT+longjmp */
580 	    rl_reset_after_signal ();
581 	}
582 #endif
583 
584 	load_file_error();	/* if we were in load_file(), cleanup */
585 	SET_CURSOR_ARROW;
586 
587 #ifdef VMS
588 	/* after catching interrupt */
589 	/* VAX stuffs up stdout on SIGINT while writing to stdout,
590 	   so reopen stdout. */
591 	if (gpoutfile == stdout) {
592 	    if ((stdout = freopen("SYS$OUTPUT", "w", stdout)) == NULL) {
593 		/* couldn't reopen it so try opening it instead */
594 		if ((stdout = fopen("SYS$OUTPUT", "w")) == NULL) {
595 		    /* don't use int_error here - causes infinite loop! */
596 		    fputs("Error opening SYS$OUTPUT as stdout\n", stderr);
597 		}
598 	    }
599 	    gpoutfile = stdout;
600 	}
601 #endif /* VMS */
602 
603 	/* Why a goto?  Because we exited the loop below via int_error */
604 	/* using LONGJMP.  The compiler was not expecting this, and    */
605 	/* "optimized" the handling of argc and argv such that simply  */
606 	/* entering the loop again from the top finds them messed up.  */
607 	/* If we reenter the loop via a goto then there is some hope   */
608 	/* that code reordering does not hurt us.                      */
609 	if (reading_from_dash && interactive)
610 	    goto RECOVER_FROM_ERROR_IN_DASH;
611 	reading_from_dash = FALSE;
612 
613 	if (!interactive && !noinputfiles) {
614 	    term_reset();
615 	    gp_exit(EXIT_FAILURE);	/* exit on non-interactive error */
616 	}
617     }
618 
619     /* load filenames given as arguments */
620     while (--argc > 0) {
621 	    ++argv;
622 	    c_token = 0;
623 	    if (!strncmp(*argv, "-persist", 2) || !strcmp(*argv, "--persist")
624 #ifdef _WIN32
625 		|| !stricmp(*argv, "-noend") || !stricmp(*argv, "/noend")
626 #endif
627 	    ) {
628 		FPRINTF((stderr,"'persist' command line option recognized\n"));
629 	    } else if (strcmp(*argv, "-") == 0) {
630 #if defined(_WIN32) && !defined(WGP_CONSOLE)
631 		TextShow(&textwin);
632 		interactive = TRUE;
633 #else
634 		interactive = isatty(fileno(stdin));
635 #endif
636 
637 RECOVER_FROM_ERROR_IN_DASH:
638 		reading_from_dash = TRUE;
639 		while (!com_line());
640 		reading_from_dash = FALSE;
641 		interactive = FALSE;
642 		noinputfiles = FALSE;
643 
644 	    } else if (strcmp(*argv, "-e") == 0) {
645 		int save_state = interactive;
646 		--argc; ++argv;
647 		if (argc <= 0) {
648 		    fprintf(stderr, "syntax:  gnuplot -e \"commands\"\n");
649 		    return 0;
650 		}
651 		interactive = FALSE;
652 		noinputfiles = FALSE;
653 		do_string(*argv);
654 		interactive = save_state;
655 
656 	    } else if (!strncmp(*argv, "-slow", 2) || !strcmp(*argv, "--slow")) {
657 		slow_font_startup = TRUE;
658 
659 	    } else if (!strncmp(*argv, "-d", 2) || !strcmp(*argv, "--default-settings")) {
660 		/* Ignore this; it already had its effect */
661 		FPRINTF((stderr, "ignoring -d\n"));
662 
663 	    } else if (strcmp(*argv, "-c") == 0) {
664 		/* Pass command line arguments to the gnuplot script in the next
665 		 * argument. This consumes the remainder of the command line
666 		 */
667 		interactive = FALSE;
668 		noinputfiles = FALSE;
669 		--argc; ++argv;
670 		if (argc <= 0) {
671 		    fprintf(stderr, "syntax:  gnuplot -c scriptname args\n");
672 		    gp_exit(EXIT_FAILURE);
673 		}
674 		call_argc = GPMIN(9, argc - 1);
675 		for (i=0; i<=call_argc; i++) {
676 		    /* Need to stash argv[i] somewhere visible to load_file() */
677 		    call_args[i] = gp_strdup(argv[i+1]);
678 		}
679 
680 		load_file(loadpath_fopen(*argv, "r"), gp_strdup(*argv), 5);
681 		gp_exit(EXIT_SUCCESS);
682 
683 	    } else if (*argv[0] == '-') {
684 		fprintf(stderr, "unrecognized option %s\n", *argv);
685 	    } else {
686 		interactive = FALSE;
687 		noinputfiles = FALSE;
688 		load_file(loadpath_fopen(*argv, "r"), gp_strdup(*argv), 4);
689 	    }
690     }
691 
692     /* take commands from stdin */
693     if (noinputfiles) {
694 	while (!com_line())
695 	    ctrlc_flag = FALSE; /* reset asynchronous Ctrl-C flag */
696     }
697 
698 #ifdef _WIN32
699     /* On Windows, handle 'persist' by keeping the main input loop running (windows/wxt), */
700     /* but only if there are any windows open. Note that qt handles this properly. */
701     if (persist_cl) {
702 	if (WinAnyWindowOpen()) {
703 #ifdef WGP_CONSOLE
704 	    if (!interactive) {
705 		/* no further input from pipe */
706 		while (WinAnyWindowOpen())
707 		win_sleep(100);
708 	    } else
709 #endif
710 	    {
711 		interactive = TRUE;
712 		while (!com_line())
713 		    ctrlc_flag = FALSE; /* reset asynchronous Ctrl-C flag */
714 		interactive = FALSE;
715 	    }
716 	}
717     }
718 #endif
719 
720 #if (defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)) && defined(GNUPLOT_HISTORY)
721 #if !defined(HAVE_ATEXIT) && !defined(HAVE_ON_EXIT)
722     /* You should be here if you neither have 'atexit()' nor 'on_exit()' */
723     wrapper_for_write_history();
724 #endif /* !HAVE_ATEXIT && !HAVE_ON_EXIT */
725 #endif /* (HAVE_LIBREADLINE || HAVE_LIBEDITLINE) && GNUPLOT_HISTORY */
726 
727 #ifdef OS2
728     RexxDeregisterSubcom("GNUPLOT", NULL);
729 #endif
730 
731     /* HBB 20040223: Not all compilers like exit() to end main() */
732     /* exit(exit_status); */
733 #if ! defined(_WIN32)
734     /* Windows does the cleanup later */
735     gp_exit_cleanup();
736 #endif
737     return exit_status;
738 }
739 
740 
741 /* Set up to catch interrupts */
742 void
interrupt_setup()743 interrupt_setup()
744 {
745     (void) signal(SIGINT, (sigfunc) inter);
746 
747 #ifdef SIGPIPE
748     /* ignore pipe errors, this might happen with set output "|head" */
749     (void) signal(SIGPIPE, SIG_IGN);
750 #endif /* SIGPIPE */
751 }
752 
753 
754 /*
755  * Initialize 'constants' stored as variables (user could mangle these)
756  */
757 void
init_constants()758 init_constants()
759 {
760     (void) Gcomplex(&udv_pi.udv_value, M_PI, 0.0);
761     udv_NaN = get_udv_by_name("NaN");
762     (void) Gcomplex(&(udv_NaN->udv_value), not_a_number(), 0.0);
763 }
764 
765 /*
766  * Initialize graphics context, color palette, local preferences.
767  * Called both at program startup and by "reset session".
768  */
769 void
init_session()770 init_session()
771 {
772 	/* Disable pipes and system commands during initialization */
773 	successful_initialization = FALSE;
774 
775 	/* Undefine any previously-used variables */
776 	del_udv_by_name("",TRUE);
777 
778 	/* Restore default colors before loading local preferences */
779 	set_colorsequence(1);
780 
781 	/* Reset program variables not handled by 'reset' */
782 	overflow_handling = INT64_OVERFLOW_TO_FLOAT;
783 
784 	/* Reset voxel data structures if supported */
785 	init_voxelsupport();
786 
787 	/* Make sure all variables start in the same state 'reset'
788 	 * would set them to.
789 	 */
790 	reset_command();	/* FIXME: this does c_token++ */
791 	load_rcfile(0);		/* System-wide gnuplotrc if configured */
792 	load_rcfile(1);		/* ./.gnuplot if configured */
793 
794 	/* After this point we allow pipes and system commands */
795 	successful_initialization = TRUE;
796 
797 	load_rcfile(2);		/* ~/.gnuplot */
798 }
799 
800 /*
801  * Read commands from an initialization file.
802  * where = 0: look for gnuplotrc in system shared directory
803  * where = 1: look for .gnuplot in current directory
804  * where = 2: look for .gnuplot in home directory
805  */
806 static void
load_rcfile(int where)807 load_rcfile(int where)
808 {
809     FILE *plotrc = NULL;
810     char *rcfile = NULL;
811 
812     if (skip_gnuplotrc)
813 	return;
814 
815     if (where == 0) {
816 #ifdef GNUPLOT_SHARE_DIR
817 # if defined(_WIN32) || defined(MSDOS) || defined(OS2)
818 	rcfile = RelativePathToGnuplot(GNUPLOT_SHARE_DIR "\\gnuplotrc");
819 # else
820 	rcfile = (char *) gp_alloc(strlen(GNUPLOT_SHARE_DIR) + 1 + strlen("gnuplotrc") + 1, "rcfile");
821 	strcpy(rcfile, GNUPLOT_SHARE_DIR);
822 	PATH_CONCAT(rcfile, "gnuplotrc");
823 # endif
824 	plotrc = fopen(rcfile, "r");
825 #endif
826 
827     } else if (where == 1) {
828 #ifdef USE_CWDRC
829     /* Allow check for a .gnuplot init file in the current directory */
830     /* This is a security risk, as someone might leave a malicious   */
831     /* init file in a shared directory.                              */
832 	plotrc = fopen(PLOTRC, "r");
833 #endif /* !USE_CWDRC */
834 
835     } else if (where == 2 && user_homedir) {
836 	/* length of homedir + directory separator + length of file name + \0 */
837 	int len = (user_homedir ? strlen(user_homedir) : 0) + 1 + strlen(PLOTRC) + 1;
838 	rcfile = gp_alloc(len, "rcfile");
839 	strcpy(rcfile, user_homedir);
840 	PATH_CONCAT(rcfile, PLOTRC);
841 	plotrc = fopen(rcfile, "r");
842     }
843 
844     if (plotrc) {
845 	char *rc = gp_strdup(rcfile ? rcfile : PLOTRC);
846 	load_file(plotrc, rc, 3);
847 	push_terminal(0); /* needed if terminal or its options were changed */
848     }
849 
850     free(rcfile);
851 }
852 
853 
854 void
get_user_env()855 get_user_env()
856 {
857     if (user_homedir == NULL) {
858 	const char *env_home;
859 
860 	if ((env_home = getenv(HOME))
861 #ifdef _WIN32
862 	    || (env_home = appdata_directory())
863 	    || (env_home = getenv("USERPROFILE"))
864 #endif
865 	    || (env_home = getenv("HOME"))
866 	   )
867 	    user_homedir = (const char *) gp_strdup(env_home);
868 	else if (interactive)
869 	    int_warn(NO_CARET, "no HOME found");
870     }
871     if (user_shell == NULL) {
872 	const char *env_shell;
873 
874 	if ((env_shell = getenv("SHELL")) == NULL)
875 #if defined(MSDOS) || defined(_WIN32) || defined(OS2)
876 	    if ((env_shell = getenv("COMSPEC")) == NULL)
877 #endif
878 		env_shell = SHELL;
879 
880 	user_shell = (const char *) gp_strdup(env_shell);
881     }
882 }
883 
884 /* expand tilde in path
885  * path cannot be a static array!
886  * tilde must be the first character in *pathp;
887  * we may change that later
888  */
889 void
gp_expand_tilde(char ** pathp)890 gp_expand_tilde(char **pathp)
891 {
892     if (!*pathp)
893 	int_error(NO_CARET, "Cannot expand empty path");
894 
895     if ((*pathp)[0] == '~' && (*pathp)[1] == DIRSEP1) {
896 	if (user_homedir) {
897 	    size_t n = strlen(*pathp);
898 
899 	    *pathp = gp_realloc(*pathp, n + strlen(user_homedir), "tilde expansion");
900 	    /* include null at the end ... */
901 	    memmove(*pathp + strlen(user_homedir) - 1, *pathp, n + 1);
902 	    memcpy(*pathp, user_homedir, strlen(user_homedir));
903 	} else
904 	    int_warn(NO_CARET, "HOME not set - cannot expand tilde");
905     }
906 }
907 
908 
909 static void
init_memory()910 init_memory()
911 {
912     extend_input_line();
913     extend_token_table();
914     replot_line = gp_strdup("");
915 }
916 
917 
918 #ifdef OS2
919 
920 int
ExecuteMacro(char * argv,int namelength)921 ExecuteMacro(char *argv, int namelength)
922 {
923     RXSTRING rxRc;
924     RXSTRING rxArg[2];
925     int rxArgCount = 0;
926     char pszName[CCHMAXPATH];
927     char *rxArgStr;
928     short sRc;
929     long rc;
930 
931     if (namelength >= sizeof(pszName))
932 	return 1;
933     safe_strncpy(pszName, argv, sizeof(pszName));
934     rxArgStr = &argv[namelength];
935     RXSTRPTR(rxRc) = NULL;
936 
937     /*
938        REXX standard calling (gnuplot 3.7pl1 and above):
939        The program name is not supplied and so all actual arguments
940        are in a single string:
941        Parse Arg param
942        We even handle blanks like cmd.exe when calling REXX programs.
943      */
944 
945     if (*rxArgStr) {
946 	MAKERXSTRING(rxArg[0], rxArgStr, strlen(rxArgStr));
947 	rxArgCount++;
948     }
949 
950     CallFromRexx = TRUE;
951     rc = RexxStart(
952 		      rxArgCount,
953 		      rxArg,
954 		      pszName,
955 		      NULL,
956 		      "GNUPLOT",
957 		      RXCOMMAND,
958 		      NULL,
959 		      &sRc,
960 		      &rxRc);
961     CallFromRexx = FALSE;
962 
963    /* am: a word WRT errors codes:
964       the negative ones don't seem to have symbolic names, you can get
965       them from the OREXX reference, they're not in REXX Programming Guide -
966       no idea where to retrieve them from a Warp 3 reference ??
967       The positive ones are somehow referenced in REXXPG
968    */
969     if (rc < 0) {
970 	/* REXX error */
971     } else if (rc > 0) {
972 	/* Interpreter couldn't be started */
973 	if (rc == -4)
974 	   /* run was cancelled, but don't give error message */
975 	    rc = 0;
976     } else if (rc==0) {
977 	/* all was fine */
978     }
979 
980 /* We don't we try to use rxRc ?
981    BTW, don't use free() instead since it's allocated inside RexxStart()
982    and not in our executable using the EMX libraries */
983    if (RXSTRPTR(rxRc))
984        /* I guess it's NULL if something major went wrong,
985 	  NULL strings are usually not part of the REXX language ... */
986        DosFreeMem(rxRc.strptr);
987 
988    return rc;
989 }
990 
991 /* Rexx command line interface */
992 ULONG
RexxInterface(PRXSTRING rxCmd,PUSHORT pusErr,PRXSTRING rxRc)993 RexxInterface(PRXSTRING rxCmd, PUSHORT pusErr, PRXSTRING rxRc)
994 {
995     int rc;
996     static JMP_BUF keepenv;
997     int cmdlen;
998 
999     memcpy(keepenv, command_line_env, sizeof(JMP_BUF));
1000     if (!SETJMP(command_line_env, 1)) {
1001 	/* Set variable gp_input_line.
1002 	   Watch out for line length of NOT_ZERO_TERMINATED strings ! */
1003 	cmdlen = rxCmd->strlength + 1;
1004 	safe_strncpy(gp_input_line, rxCmd->strptr, gp_input_line_len);
1005 	gp_input_line[cmdlen] = NUL;
1006 	rc = do_line();
1007 	*pusErr = RXSUBCOM_OK;
1008 	rxRc->strptr[0] = rc + '0';
1009 	rxRc->strptr[1] = NUL;
1010 	rxRc->strlength = strlen(rxRc->strptr);
1011     } else {
1012 /*
1013    We end up here when bail_to_command_line() is called.
1014    Therefore sometimes this call should be avoided when
1015    executing a REXX program (e.g. 'Cancel' from
1016    PM GUI after a 'pause -1' command)
1017 */
1018 	*pusErr = RXSUBCOM_ERROR;
1019 	RexxSetHalt(getpid(), 1);
1020     }
1021     memcpy(command_line_env, keepenv, sizeof(JMP_BUF));
1022     return 0;
1023 }
1024 #endif /* OS2 */
1025 
1026 #ifdef GNUPLOT_HISTORY
1027 
1028 /* cancel_history() can be called by terminals that fork a helper process
1029  * to make sure that the helper doesn't trash the history file on exit.
1030  */
1031 void
cancel_history()1032 cancel_history()
1033 {
1034     expanded_history_filename = NULL;
1035 }
1036 
1037 # if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
1038 
1039 static void
wrapper_for_write_history()1040 wrapper_for_write_history()
1041 {
1042     if (!expanded_history_filename)
1043 	return;
1044     if (history_is_stifled())
1045 	unstifle_history();
1046     if (gnuplot_history_size >= 0)
1047 	stifle_history (gnuplot_history_size);
1048 
1049     /* returns 0 on success */
1050     if (write_history(expanded_history_filename))
1051 	fprintf (stderr, "Warning:  Could not write history file!!!\n");
1052 
1053     unstifle_history();
1054 }
1055 
1056 # else /* HAVE_LIBREADLINE || HAVE_LIBEDITLINE */
1057 
1058 /* version for gnuplot's own write_history */
1059 static void
wrapper_for_write_history()1060 wrapper_for_write_history()
1061 {
1062     /* What we really want to do is truncate(expanded_history_filename),
1063        but this is only available on BSD compatible systems */
1064     if (!expanded_history_filename)
1065 	return;
1066     remove(expanded_history_filename);
1067     if (gnuplot_history_size < 0)
1068 	write_history(expanded_history_filename);
1069     else
1070 	write_history_n(gnuplot_history_size, expanded_history_filename, "w");
1071 }
1072 
1073 # endif /* HAVE_LIBREADLINE || HAVE_LIBEDITLINE */
1074 #endif /* GNUPLOT_HISTORY */
1075 
1076 void
restrict_popen()1077 restrict_popen()
1078 {
1079     if (!successful_initialization)
1080 	int_error(NO_CARET,"Pipes and shell commands not permitted during initialization");
1081 }
1082 
1083 
1084 #if defined(MSDOS) || defined(OS2)
1085 /* retrieve path relative to gnuplot executable */
1086 char *
RelativePathToGnuplot(const char * path)1087 RelativePathToGnuplot(const char * path)
1088 {
1089     char * rel_path, *s;
1090 
1091     rel_path = (char * ) gp_alloc(strlen(progpath) + strlen(path) + 1, "RelativePathToGnuplot");
1092     strcpy(rel_path, progpath);
1093     /* progpath is guaranteed to have a trailing slash */
1094     strcat(rel_path, path);
1095     /* convert slashes to backslashes */
1096     for (s = rel_path;*s != NUL; s++)
1097 	if (*s == DIRSEP2)
1098 	    *s = DIRSEP1;
1099     return rel_path;
1100 }
1101 #endif
1102