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