Icons are changed
[gnuplot] / src / command.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: command.c,v 1.144.2.11 2008/12/12 06:57:50 sfeam Exp $"); }
3 #endif
4
5 /* GNUPLOT - command.c */
6
7 /*[
8  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
9  *
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36
37 /*
38  * Changes:
39  *
40  * Feb 5, 1992  Jack Veenstra   (veenstra@cs.rochester.edu) Added support to
41  * filter data values read from a file through a user-defined function before
42  * plotting. The keyword "thru" was added to the "plot" command. Example
43  * syntax: f(x) = x / 100 plot "test.data" thru f(x) This example divides all
44  * the y values by 100 before plotting. The filter function processes the
45  * data before any log-scaling occurs. This capability should be generalized
46  * to filter x values as well and a similar feature should be added to the
47  * "splot" command.
48  *
49  * 19 September 1992  Lawrence Crowl  (crowl@cs.orst.edu)
50  * Added user-specified bases for log scaling.
51  *
52  * April 1999 Franz Bakan (bakan@ukezyk.desy.de)
53  * Added code to support mouse-input from OS/2 PM window
54  * Changes marked by USE_MOUSE
55  *
56  * May 1999, update by Petr Mikulik
57  * Use gnuplot's pid in shared mem name
58  *
59  * August 1999 Franz Bakan and Petr Mikulik
60  * Encapsulating read_line into a thread, acting on input when thread or
61  * gnupmdrv posts an event semaphore. Thus mousing works even when gnuplot
62  * is used as a plotting device (commands passed via pipe).
63  *
64  */
65
66 #include "command.h"
67
68 #include "alloc.h"
69 #include "eval.h"
70 #include "fit.h"
71 #include "binary.h"
72 #include "datafile.h"
73 #include "getcolor.h"
74 #include "gp_hist.h"
75 #include "gp_time.h"
76 #include "misc.h"
77 #include "parse.h"
78 #include "plot.h"
79 #include "plot2d.h"
80 #include "plot3d.h"
81 #include "readline.h"
82 #include "save.h"
83 #include "scanner.h"
84 #include "setshow.h"
85 #include "tables.h"
86 #include "term_api.h"
87 #include "util.h"
88
89 #ifdef USE_MOUSE
90 # include "mouse.h"
91 #endif
92
93 #define PROMPT "gnuplot> "
94
95 #if (defined(MSDOS) || defined(DOS386)) && defined(__TURBOC__) && !defined(_Windows)
96 unsigned _stklen = 16394;        /* increase stack size */
97 #endif /* MSDOS && TURBOC */
98
99 #ifdef OS2_IPC
100 #  define INCL_DOSMEMMGR
101 #  define INCL_DOSPROCESS
102 #  define INCL_DOSSEMAPHORES
103 #  include <os2.h>
104 PVOID input_from_PM_Terminal = NULL;
105 char mouseSharedMemName[40] = "";
106 HEV semInputReady = 0;      /* semaphore to be created in plot.c */
107 int thread_rl_Running = 0;  /* running status */
108 int thread_rl_RetCode = -1; /* return code from readline in a thread */
109 #endif /* OS2_IPC */
110
111
112 #ifndef _Windows
113 # include "help.h"
114 #else
115 # ifdef USE_OWN_WINSYSTEM_FUNCTION
116 static int winsystem __PROTO((const char *));
117 # endif
118 #endif /* _Windows */
119
120 #ifdef _Windows
121 # include <windows.h>
122 # ifdef __MSC__
123 #  include <malloc.h>
124 #  include <direct.h>          /* getcwd() */
125 # else
126 #  include <alloc.h>
127 #  ifndef __WATCOMC__
128 #   include <dir.h>             /* setdisk() */
129 #  endif
130 # endif                         /* !MSC */
131 # include "win/winmain.h"
132 #endif /* _Windows */
133
134 #ifdef VMS
135 int vms_vkid;                   /* Virtual keyboard id */
136 int vms_ktid;                   /* key table id, for translating keystrokes */
137 #endif /* VMS */
138
139
140 /* static prototypes */
141 static void command __PROTO((void));
142 static int changedir __PROTO((char *path));
143 static char* fgets_ipc __PROTO((char* dest, int len));
144 static int read_line __PROTO((const char *prompt));
145 static void do_system __PROTO((const char *));
146 static void test_palette_subcommand __PROTO((void));
147 static void test_time_subcommand __PROTO((void));
148
149 #ifdef AMIGA_AC_5
150 static void getparms __PROTO((char *, char **));
151 #endif
152 #ifdef GP_MACROS
153 static int string_expand __PROTO((void));
154 TBOOLEAN expand_macros = FALSE;
155 #endif
156
157 struct lexical_unit *token;
158 int token_table_size;
159
160
161 char *gp_input_line;
162 size_t gp_input_line_len;
163 int inline_num;                 /* input line number */
164
165 struct udft_entry *dummy_func;
166
167 /* support for replot command */
168 char *replot_line;
169 int plot_token;                 /* start of 'plot' command */
170
171 /* flag to disable `replot` when some data are sent through stdin;
172  * used by mouse/hotkey capable terminals */
173 TBOOLEAN replot_disabled = FALSE;
174
175 #ifdef USE_MOUSE
176 int paused_for_mouse = 0;
177 #endif
178
179 /* output file for the print command */
180 FILE *print_out = NULL;
181 char *print_out_name = NULL;
182
183 /* input data, parsing variables */
184 #ifdef AMIGA_SC_6_1
185 __far int num_tokens, c_token;
186 #else
187 int num_tokens, c_token;
188 #endif
189
190 static int if_depth = 0;
191 static TBOOLEAN if_condition = FALSE;
192
193 static int command_exit_status = 0;
194
195 /* support for dynamic size of input line */
196 void
197 extend_input_line()
198 {
199     if (gp_input_line_len == 0) {
200         /* first time */
201         gp_input_line = gp_alloc(MAX_LINE_LEN, "gp_input_line");
202         gp_input_line_len = MAX_LINE_LEN;
203         gp_input_line[0] = NUL;
204
205 #ifdef OS2_IPC
206         sprintf( mouseSharedMemName, "\\SHAREMEM\\GP%i_Mouse_Input", getpid() );
207         if (DosAllocSharedMem((PVOID) & input_from_PM_Terminal,
208                 mouseSharedMemName, MAX_LINE_LEN, PAG_WRITE | PAG_COMMIT))
209             fputs("command.c: DosAllocSharedMem ERROR\n",stderr);
210 #endif /* OS2_IPC */
211
212     } else {
213         gp_input_line = gp_realloc(gp_input_line, gp_input_line_len + MAX_LINE_LEN,
214                                 "extend input line");
215         gp_input_line_len += MAX_LINE_LEN;
216         FPRINTF((stderr, "extending input line to %d chars\n",
217                  gp_input_line_len));
218     }
219 }
220
221 /* constant by which token table grows */
222 #define MAX_TOKENS 400
223
224 void
225 extend_token_table()
226 {
227     if (token_table_size == 0) {
228         /* first time */
229         token = (struct lexical_unit *) gp_alloc(MAX_TOKENS * sizeof(struct lexical_unit), "token table");
230         token_table_size = MAX_TOKENS;
231         /* HBB: for checker-runs: */
232         memset(token, 0, MAX_TOKENS * sizeof(*token));
233     } else {
234         token = gp_realloc(token, (token_table_size + MAX_TOKENS) * sizeof(struct lexical_unit), "extend token table");
235         memset(token+token_table_size, 0, MAX_TOKENS * sizeof(*token));
236         token_table_size += MAX_TOKENS;
237         FPRINTF((stderr, "extending token table to %d elements\n", token_table_size));
238     }
239 }
240
241
242 #ifdef OS2_IPC
243 void thread_read_line()
244 {
245    thread_rl_Running = 1;
246    thread_rl_RetCode = ( read_line(PROMPT) );
247    thread_rl_Running = 0;
248    DosPostEventSem(semInputReady);
249 }
250 #endif /* OS2_IPC */
251
252
253 int
254 com_line()
255 {
256 #ifdef OS2_IPC
257 static char *input_line_SharedMem = NULL;
258
259     if (input_line_SharedMem == NULL) {  /* get shared mem only once */
260     if (DosGetNamedSharedMem((PVOID) &input_line_SharedMem,
261                 mouseSharedMemName, PAG_WRITE | PAG_READ))
262         fputs("readline.c: DosGetNamedSharedMem ERROR\n", stderr);
263     else
264         *input_line_SharedMem = 0;
265     }
266 #endif /* OS2_IPC */
267
268     if (multiplot) {
269         /* calls int_error() if it is not happy */
270         term_check_multiplot_okay(interactive);
271
272         if (read_line("multiplot> "))
273             return (1);
274     } else {
275
276 #ifndef USE_MOUSE
277         if (read_line(PROMPT))
278             return (1);
279 #else
280 # ifdef OS2_IPC
281         ULONG u;
282         if (thread_rl_Running == 0) {
283             int res = _beginthread(thread_read_line,NULL,32768,NULL);
284             if (res == -1)
285                 fputs("error command.c could not begin thread\n",stderr);
286         }
287         /* wait until a line is read or gnupmdrv makes shared mem available */
288         DosWaitEventSem(semInputReady,SEM_INDEFINITE_WAIT);
289         DosResetEventSem(semInputReady,&u);
290         if (thread_rl_Running) {
291             if (input_line_SharedMem == NULL || !*input_line_SharedMem)
292                 return (0);
293             if (*input_line_SharedMem=='%') {
294                 do_event( (struct gp_event_t*)(input_line_SharedMem+1) ); /* pass terminal's event */
295                 input_line_SharedMem[0] = 0; /* discard the whole command line */
296                 thread_rl_RetCode = 0;
297                 return (0);
298             }
299             if (*input_line_SharedMem &&
300                 strstr(input_line_SharedMem,"plot") != NULL &&
301                 (strcmp(term->name,"pm") && strcmp(term->name,"x11"))) {
302                 /* avoid plotting if terminal is not PM or X11 */
303                 fprintf(stderr,"\n\tCommand(s) ignored for other than PM and X11 terminals\a\n");
304                 if (interactive) fputs(PROMPT,stderr);
305                 input_line_SharedMem[0] = 0; /* discard the whole command line */
306                 return (0);
307             }
308 #  if 0
309             fprintf(stderr,"shared mem received: |%s|\n",input_line_SharedMem);
310             if (*input_line_SharedMem && input_line_SharedMem[strlen(input_line_SharedMem)-1] != '\n') fprintf(stderr,"\n");
311 #  endif
312             strcpy(gp_input_line, input_line_SharedMem);
313             input_line_SharedMem[0] = 0;
314             thread_rl_RetCode = 0;
315         }
316         if (thread_rl_RetCode)
317             return (1);
318 # else /* OS2_IPC */
319         if (read_line(PROMPT))
320             return (1);
321 # endif /* OS2_IPC */
322 #endif /* USE_MOUSE */
323     }
324
325     /* So we can flag any new output: if false at time of error,
326      * we reprint the command line before printing caret.
327      * TRUE for interactive terminals, since the command line is typed.
328      * FALSE for non-terminal stdin, so command line is printed anyway.
329      * (DFK 11/89)
330      */
331     screen_ok = interactive;
332
333     if (do_line())
334         return (1);
335     else
336         return (0);
337 }
338
339
340 int
341 do_line()
342 {
343     /* Line continuation has already been handled
344      * by read_line() */
345     char *inlptr = gp_input_line;
346
347     /* Skip leading whitespace */
348     while (isspace((unsigned char) *inlptr))
349         inlptr++;
350
351     if (inlptr != gp_input_line) {
352         /* If there was leading whitespace, copy the actual
353          * command string to the front. use memmove() because
354          * source and target may overlap */
355         memmove(gp_input_line, inlptr, strlen(inlptr));
356         /* Terminate resulting string */
357         gp_input_line[strlen(inlptr)] = NUL;
358     }
359     FPRINTF((stderr, "Input line: \"%s\"\n", gp_input_line));
360
361 #ifdef GP_MACROS
362     /* Expand any string variables in the current input line.
363      * Allow up to 4 levels of recursion */
364     if (expand_macros)
365     if (string_expand() && string_expand() && string_expand() && string_expand() && string_expand())
366         int_error(NO_CARET, "Too many levels of nested macros");
367 #endif
368
369     /* also used in load_file */
370     if (is_system(gp_input_line[0])) {
371         do_system(gp_input_line + 1);
372         if (interactive)        /* 3.5 did it unconditionally */
373             (void) fputs("!\n", stderr);        /* why do we need this ? */
374         return (0);
375     }
376
377     if_depth = 0;
378     if_condition = TRUE;
379     num_tokens = scanner(&gp_input_line, &gp_input_line_len);
380     c_token = 0;
381     while (c_token < num_tokens) {
382         command();
383         if (command_exit_status) {
384             command_exit_status = 0;
385             return 1;
386         }
387         if (c_token < num_tokens) {     /* something after command */
388             if (equals(c_token, ";"))
389                 c_token++;
390             else
391                 int_error(c_token, "';' expected");
392         }
393     }
394     return (0);
395 }
396
397
398 void
399 do_string(char *s)
400 {
401     char *orig_input_line = gp_strdup(gp_input_line);
402
403     while (gp_input_line_len < strlen(s) + 1)
404         extend_input_line();
405     strcpy(gp_input_line, s);
406
407 #ifdef USE_MOUSE
408     if (display_ipc_commands())
409         fprintf(stderr, "%s\n", s);
410 #endif
411
412     do_line();
413
414     strcpy(gp_input_line, orig_input_line);
415     free(orig_input_line);
416 }
417
418
419 #ifdef USE_MOUSE
420 void
421 toggle_display_of_ipc_commands()
422 {
423     if (mouse_setting.verbose)
424         mouse_setting.verbose = 0;
425     else
426         mouse_setting.verbose = 1;
427 }
428
429 int
430 display_ipc_commands()
431 {
432     return mouse_setting.verbose;
433 }
434
435 void
436 do_string_replot(char *s)
437 {
438     char *orig_input_line = gp_strdup(gp_input_line);
439
440     while (gp_input_line_len < strlen(s) + 1)
441         extend_input_line();
442     strcpy(gp_input_line, s);
443     if (display_ipc_commands())
444         fprintf(stderr, "%s\n", s);
445
446     do_line();
447     if (!replot_disabled)
448         replotrequest();
449
450     strcpy(gp_input_line, orig_input_line);
451     free(orig_input_line);
452 }
453
454 void
455 restore_prompt()
456 {
457     if (interactive) {
458 #if defined(HAVE_LIBREADLINE)
459         rl_forced_update_display();
460 #else
461 #if defined(HAVE_LIBEDITLINE)
462         /* FIXME: editline does not support forced update,
463                   so this is probably not enough */
464         rl_redisplay();
465 #endif
466         fputs(PROMPT, stderr);
467         fflush(stderr);
468 #endif
469     }
470 }
471 #endif /* USE_MOUSE */
472
473
474 void
475 define()
476 {
477     int start_token;    /* the 1st token in the function definition */
478     struct udvt_entry *udv;
479     struct udft_entry *udf;
480     struct value result;
481
482     if (equals(c_token + 1, "(")) {
483         /* function ! */
484         int dummy_num = 0;
485         struct at_type *at_tmp;
486         char save_dummy[MAX_NUM_VAR][MAX_ID_LEN+1];
487         memcpy(save_dummy, c_dummy_var, sizeof(save_dummy));
488         start_token = c_token;
489         do {
490             c_token += 2;       /* skip to the next dummy */
491             copy_str(c_dummy_var[dummy_num++], c_token, MAX_ID_LEN);
492         } while (equals(c_token + 1, ",") && (dummy_num < MAX_NUM_VAR));
493         if (equals(c_token + 1, ","))
494             int_error(c_token + 2, "function contains too many parameters");
495         c_token += 3;           /* skip (, dummy, ) and = */
496         if (END_OF_COMMAND)
497             int_error(c_token, "function definition expected");
498         udf = dummy_func = add_udf(start_token);
499         udf->dummy_num = dummy_num;
500         if ((at_tmp = perm_at()) == (struct at_type *) NULL)
501             int_error(start_token, "not enough memory for function");
502         if (udf->at)            /* already a dynamic a.t. there */
503             free_at(udf->at);   /* so free it first */
504         udf->at = at_tmp;       /* before re-assigning it. */
505         memcpy(c_dummy_var, save_dummy, sizeof(save_dummy));
506         m_capture(&(udf->definition), start_token, c_token - 1);
507         dummy_func = NULL;      /* dont let anyone else use our workspace */
508     } else {
509         /* variable ! */
510         char *varname = gp_input_line + token[c_token].start_index;
511         if (!strncmp(varname, "GPVAL_", 6) || !strncmp(varname, "MOUSE_", 6))
512             int_error(c_token, "Cannot set internal variables GPVAL_ and MOUSE_");
513         start_token = c_token;
514         c_token += 2;
515         udv = add_udv(start_token);
516         (void) const_express(&result);
517 #ifdef GP_STRING_VARS
518         /* Prevents memory leak if the variable name is re-used */
519         if (!udv->udv_undef)
520             gpfree_string(&udv->udv_value);
521 #endif
522         udv->udv_value = result;
523         udv->udv_undef = FALSE;
524     }
525 }
526
527
528 void
529 undefine_command()
530 {
531     char key[MAX_ID_LEN+1];
532     struct udvt_entry **udv_ptr = &first_udv;
533     c_token++;
534     while (!END_OF_COMMAND) {
535         copy_str(key, c_token, MAX_ID_LEN);
536         if (strncmp(key, "GPVAL_", 6) && strncmp(key, "MOUSE_", 6)) {
537             udv_ptr = &first_udv;
538             while (*udv_ptr) {
539                 if (!strcmp(key, (*udv_ptr)->udv_name)) {
540                     (*udv_ptr)->udv_undef = TRUE;
541                     gpfree_string(&((*udv_ptr)->udv_value));
542                     break;
543                 }
544                 udv_ptr = &((*udv_ptr)->next_udv);
545             }
546         }
547         c_token++;
548     }
549 }
550
551
552 static void
553 command()
554 {
555     int i;
556
557     for (i = 0; i < MAX_NUM_VAR; i++)
558         c_dummy_var[i][0] = NUL;        /* no dummy variables */
559
560     if (is_definition(c_token))
561         define();
562     else
563         (*lookup_ftable(&command_ftbl[0],c_token))();
564
565     return;
566 }
567
568
569 /* process the 'raise' or 'lower' command */
570 void
571 raise_lower_command(int lower)
572 {
573     ++c_token;
574
575     if (END_OF_COMMAND) {
576         if (lower) {
577 #ifdef OS2
578             pm_lower_terminal_window();
579 #endif
580 #ifdef X11
581             x11_lower_terminal_group();
582 #endif
583 #ifdef _Windows
584             win_lower_terminal_window();
585 #endif
586 #ifdef WXWIDGETS
587             wxt_lower_terminal_group();
588 #endif
589         } else {
590 #ifdef OS2
591             pm_raise_terminal_window();
592 #endif
593 #ifdef X11
594             x11_raise_terminal_group();
595 #endif
596 #ifdef _Windows
597             win_raise_terminal_window();
598 #endif
599 #ifdef WXWIDGETS
600             wxt_raise_terminal_group();
601 #endif
602         }
603         return;
604     } else {
605         int number;
606         int negative = equals(c_token, "-");
607
608         if (negative || equals(c_token, "+")) c_token++;
609         if (!END_OF_COMMAND && isanumber(c_token)) {
610             struct value a;
611             number = real(const_express(&a));
612             if (negative)
613             number = -number;
614             if (lower) {
615 #ifdef OS2
616                 pm_lower_terminal_window();
617 #endif
618 #ifdef X11
619                 x11_lower_terminal_window(number);
620 #endif
621 #ifdef _Windows
622                 win_lower_terminal_window();
623 #endif
624 #ifdef WXWIDGETS
625                 wxt_lower_terminal_window(number);
626 #endif
627             } else {
628 #ifdef OS2
629                 pm_raise_terminal_window();
630 #endif
631 #ifdef X11
632                 x11_raise_terminal_window(number);
633 #endif
634 #ifdef _Windows
635                 win_raise_terminal_window();
636 #endif
637 #ifdef WXWIDGETS
638                 wxt_raise_terminal_window(number);
639 #endif
640             }
641             ++c_token;
642             return;
643         }
644     }
645     int_error(c_token, "usage: raise {x11_plot_n}");
646 }
647
648 void
649 raise_command(void)
650 {
651     raise_lower_command(0);
652 }
653
654 void
655 lower_command(void)
656 {
657     raise_lower_command(1);
658 }
659
660
661 #ifdef USE_MOUSE
662
663 #define WHITE_AFTER_TOKEN(x) \
664 (' ' == gp_input_line[token[x].start_index + token[x].length] \
665 || '\t' == gp_input_line[token[x].start_index + token[x].length] \
666 || '\0' == gp_input_line[token[x].start_index + token[x].length])
667
668 /* process the 'bind' command */
669 void
670 bind_command()
671 {
672     char* lhs = (char*) 0;
673     char* rhs = (char*) 0;
674     TBOOLEAN allwindows = FALSE;
675     ++c_token;
676
677     if (!END_OF_COMMAND && equals(c_token,"!")) {
678         bind_remove_all();
679         ++c_token;
680         return;
681     }
682
683     if (!END_OF_COMMAND && almost_equals(c_token,"all$windows")) {
684         allwindows = TRUE;
685         c_token++;
686     }
687
688     /* get left hand side: the key or key sequence */
689     if (!END_OF_COMMAND) {
690         char* first = gp_input_line + token[c_token].start_index;
691         int size = (int) (strchr(first, ' ') - first);
692         if (size < 0) {
693             size = (int) (strchr(first, '\0') - first);
694         }
695         if (size < 0) {
696             fprintf(stderr, "(bind_command) %s:%d\n", __FILE__, __LINE__);
697             return;
698         }
699         lhs = (char*) gp_alloc(size + 1, "bind_command->lhs");
700         if (isstring(c_token)) {
701             quote_str(lhs, c_token, token_len(c_token));
702         } else {
703             char* ptr = lhs;
704             while (!END_OF_COMMAND) {
705                 copy_str(ptr, c_token, token_len(c_token) + 1);
706                 ptr += token_len(c_token);
707                 if (WHITE_AFTER_TOKEN(c_token)) {
708                     break;
709                 }
710                 ++c_token;
711             }
712         }
713         ++c_token;
714     }
715
716     /* get right hand side: the command. allocating the size
717      * of gp_input_line is too big, but shouldn't hurt too much. */
718     if (!END_OF_COMMAND) {
719         rhs = (char*) gp_alloc(strlen(gp_input_line) + 1, "bind_command->rhs");
720         if (isstring(c_token)) {
721             /* bind <lhs> "..." */
722             quote_str(rhs, c_token, token_len(c_token));
723             c_token++;
724         } else {
725             char* ptr = rhs;
726             while (!END_OF_COMMAND) {
727                 /* bind <lhs> ... ... ... */
728                 copy_str(ptr, c_token, token_len(c_token) + 1);
729                 ptr += token_len(c_token);
730                 if (WHITE_AFTER_TOKEN(c_token)) {
731                     *ptr++ = ' ';
732                     *ptr = '\0';
733                 }
734                 c_token++;
735             }
736         }
737     }
738
739     FPRINTF((stderr, "(bind_command) |%s| |%s|\n", lhs, rhs));
740
741     /* bind_process() will eventually free lhs / rhs ! */
742     bind_process(lhs, rhs, allwindows);
743
744 }
745 #endif /* USE_MOUSE */
746
747
748 /*
749  * Command parser functions
750  */
751
752 /* process the 'call' command */
753 void
754 call_command()
755 {
756     char *save_file = NULL;
757
758     c_token++;
759     save_file = try_to_get_string();
760
761     if (!save_file)
762         int_error(c_token, "expecting filename");
763
764     gp_expand_tilde(&save_file);
765     /* Argument list follows filename */
766     load_file(loadpath_fopen(save_file, "r"), save_file, TRUE);
767     /* gp_input_line[] and token[] now destroyed! */
768     c_token = 0;
769     num_tokens = 0;
770     free(save_file);
771 }
772
773
774 /* process the 'cd' command */
775 void
776 changedir_command()
777 {
778     char *save_file = NULL;
779
780     c_token++;
781     save_file = try_to_get_string();
782     if (!save_file)
783         int_error(c_token, "expecting directory name");
784
785     gp_expand_tilde(&save_file);
786     if (changedir(save_file))
787         int_error(c_token, "Can't change to this directory");
788     free(save_file);
789 }
790
791
792 /* process the 'clear' command */
793 void
794 clear_command()
795 {
796
797     term_start_plot();
798
799     if (multiplot && term->fillbox) {
800         unsigned int xx1 = (unsigned int) (xoffset * term->xmax);
801         unsigned int yy1 = (unsigned int) (yoffset * term->ymax);
802         unsigned int width = (unsigned int) (xsize * term->xmax);
803         unsigned int height = (unsigned int) (ysize * term->ymax);
804         (*term->fillbox) (0, xx1, yy1, width, height);
805     }
806     term_end_plot();
807
808     screen_ok = FALSE;
809     c_token++;
810
811 }
812
813
814 /* process the 'exit' and 'quit' commands */
815 void
816 exit_command()
817 {
818     /* If the command is "exit gnuplot" then do so */
819     if (equals(c_token+1,"gnuplot"))
820         exit(0);
821
822     /* else graphics will be tidied up in main */
823     command_exit_status = 1;
824 }
825
826
827 /* fit_command() is in fit.c */
828
829
830 /* help_command() is below */
831
832
833 /* process the 'history' command */
834 void
835 history_command()
836 {
837 #if defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
838     c_token++;
839
840     if (!END_OF_COMMAND && equals(c_token,"?")) {
841         static char *search_str = NULL;  /* string from command line to search for */
842
843         /* find and show the entries */
844         c_token++;
845         m_capture(&search_str, c_token, c_token);  /* reallocates memory */
846         printf ("history ?%s\n", search_str);
847         if (!history_find_all(search_str))
848             int_error(c_token,"not in history");
849         c_token++;
850
851     } else if (!END_OF_COMMAND && equals(c_token,"!")) {
852         char *search_str = NULL;  /* string from command line to search for */
853         const char *line_to_do = NULL;  /* command to execute at end if necessary */
854         int c_token_copy;
855         static char *gpil_copy = NULL;
856
857         c_token++;
858         m_capture(&search_str, c_token, c_token);
859         line_to_do = history_find(search_str);
860         free(search_str);
861         if (line_to_do == NULL)
862             int_error(c_token, "not in history");
863
864         /* Must keep current input line in case there are some remaining lines
865          * to process after a semicolon.  However, could int_error() some where
866          * during do_line() so a static copy is kept.
867          */
868         free(gpil_copy);
869         gpil_copy = gp_strdup(gp_input_line);
870         c_token_copy = c_token;
871
872         while (gp_input_line_len < strlen(line_to_do) + 1)
873             extend_input_line();
874         strcpy(gp_input_line, line_to_do);
875         if (scanner(&gp_input_line, &gp_input_line_len)) {
876             if (almost_equals(0, "hi$story") && equals(1, "!"))
877                 int_error(c_token-1,"petitio principii");       /* Oops... infinite loop */
878             else {
879                 printf("  Executing:\n\t%s\n", line_to_do);
880                 do_line();
881             }
882         }
883         /* Restore previous state of line and parser, gpil_copy will be freed next time */
884         strcpy(gp_input_line, gpil_copy);
885         num_tokens = scanner(&gp_input_line, &gp_input_line_len);
886         c_token = c_token_copy + 1;
887
888     } else {
889         struct value a;
890         int n = 0;                 /* print only <last> entries */
891         TBOOLEAN append = FALSE;   /* rewrite output file or append it */
892         static char *name = NULL;  /* name of the output file; NULL for stdout */
893
894         TBOOLEAN quiet = FALSE;
895         if (!END_OF_COMMAND && almost_equals(c_token,"q$uiet")) {
896             /* option quiet to suppress history entry numbers */
897             quiet = TRUE;
898             c_token++;
899         }
900         /* show history entries */
901         if (!END_OF_COMMAND && isanumber(c_token)) {
902             n = (int)real(const_express(&a));
903         }
904         free(name);
905         if ((name = try_to_get_string())) {
906             if (!END_OF_COMMAND && almost_equals(c_token, "ap$pend")) {
907                 append = TRUE;
908                 c_token++;
909             }
910         }
911         write_history_n(n, (quiet ? "" : name), (append ? "a" : "w"));
912     }
913
914 #else
915     c_token++;
916     int_warn(NO_CARET, "You have to compile gnuplot with builtin readline or GNU readline to enable history support.");
917 #endif /* defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE) */
918 }
919
920 #define REPLACE_ELSE(tok)             \
921 do {                                  \
922     int idx = token[tok].start_index; \
923     token[tok].length = 1;            \
924     gp_input_line[idx++] = ';'; /* e */  \
925     gp_input_line[idx++] = ' '; /* l */  \
926     gp_input_line[idx++] = ' '; /* s */  \
927     gp_input_line[idx++] = ' '; /* e */  \
928 } while (0)
929
930 #if 0
931 #define PRINT_TOKEN(tok)                                                    \
932 do {                                                                        \
933     int i;                                                                  \
934     int end_index = token[tok].start_index + token[tok].length;             \
935     for (i = token[tok].start_index; i < end_index && gp_input_line[i]; i++) { \
936         fputc(gp_input_line[i], stderr);                                       \
937     }                                                                       \
938     fputc('\n', stderr);                                                    \
939     fflush(stderr);                                                         \
940 } while (0)
941 #endif
942
943 /* process the 'if' command */
944 void
945 if_command()
946 {
947     double exprval;
948     struct value t;
949
950     if_depth++;
951
952     if (!equals(++c_token, "("))        /* no expression */
953         int_error(c_token, "expecting (expression)");
954     exprval = real(const_express(&t));
955     if (exprval != 0.0) {
956         /* fake the condition of a ';' between commands */
957         int eolpos = token[num_tokens - 1].start_index + token[num_tokens - 1].length;
958         --c_token;
959         token[c_token].length = 1;
960         token[c_token].start_index = eolpos + 2;
961         gp_input_line[eolpos + 2] = ';';
962         gp_input_line[eolpos + 3] = NUL;
963
964         if_condition = TRUE;
965     } else {
966         while (c_token < num_tokens) {
967             /* skip over until the next command */
968             while (!END_OF_COMMAND) {
969                 ++c_token;
970             }
971             if (++c_token < num_tokens && (equals(c_token, "else"))) {
972                 /* break if an "else" was found */
973                 if_condition = FALSE;
974                 --c_token; /* go back to ';' */
975                 return;
976             }
977         }
978         /* no else found */
979         c_token = num_tokens = 0;
980     }
981 }
982
983 /* process the 'else' command */
984 void
985 else_command()
986 {
987     if (if_depth <= 0) {
988         int_error(c_token, "else without if");
989         return;
990     } else {
991         if_depth--;
992     }
993
994     if (TRUE == if_condition) {
995         /* First part of line was true so
996          * discard the rest of the line. */
997         c_token = num_tokens = 0;
998     } else {
999         REPLACE_ELSE(c_token);
1000         if_condition = TRUE;
1001     }
1002 }
1003
1004
1005 /* process the 'load' command */
1006 void
1007 load_command()
1008 {
1009     FILE *fp;
1010     char *save_file;
1011
1012     c_token++;
1013     save_file = try_to_get_string();
1014     if (!save_file)
1015         int_error(c_token, "expecting filename");
1016     if (c_token < num_tokens) { /* not EOL */
1017         if (!equals(c_token, ";"))
1018             int_error(c_token, "expecting end of line");
1019         else if ( c_token + 1 < num_tokens ) /* not EOL even after ';' */
1020             int_warn(c_token + 1, "ignoring rest of line");
1021     }
1022
1023     gp_expand_tilde(&save_file);
1024     fp = strcmp(save_file, "-") ? loadpath_fopen(save_file, "r") : stdout;
1025     load_file(fp, save_file, FALSE);
1026     /* gp_input_line[] and token[] now destroyed! */
1027     c_token = num_tokens = 0;
1028     free(save_file);
1029 }
1030
1031
1032
1033 /* null command */
1034 void
1035 null_command()
1036 {
1037     return;
1038 }
1039
1040
1041 /* process the 'pause' command */
1042 void
1043 pause_command()
1044 {
1045     struct value a;
1046     int text = 0;
1047     double sleep_time;
1048     char *buf = gp_alloc(MAX_LINE_LEN+1, "pause argument");
1049
1050     c_token++;
1051
1052     *buf = NUL;
1053
1054 #ifdef USE_MOUSE
1055     paused_for_mouse = 0;
1056     if (equals(c_token,"mouse")) {
1057         sleep_time = -1;
1058         c_token++;
1059
1060 /*      EAM FIXME - This is not the correct test; what we really want */
1061 /*      to know is whether or not the terminal supports mouse feedback */
1062 /*      if (term_initialised) { */
1063         if (mouse_setting.on && term) {
1064             struct udvt_entry *current;
1065             int end_condition = 0;
1066
1067             while (!(END_OF_COMMAND)) {
1068                 if (almost_equals(c_token,"key$press")) {
1069                     end_condition |= PAUSE_KEYSTROKE;
1070                     c_token++;
1071                 } else if (equals(c_token,",")) {
1072                     c_token++;
1073                 } else if (equals(c_token,"any")) {
1074                     end_condition |= PAUSE_ANY;
1075                     c_token++;
1076                 } else if (equals(c_token,"button1")) {
1077                     end_condition |= PAUSE_BUTTON1;
1078                     c_token++;
1079                 } else if (equals(c_token,"button2")) {
1080                     end_condition |= PAUSE_BUTTON2;
1081                     c_token++;
1082                 } else if (equals(c_token,"button3")) {
1083                     end_condition |= PAUSE_BUTTON3;
1084                     c_token++;
1085                 } else if (equals(c_token,"close")) {
1086                     end_condition |= PAUSE_WINCLOSE;
1087                     c_token++;
1088                 } else
1089                     break;
1090             }
1091
1092             if (end_condition)
1093                 paused_for_mouse = end_condition;
1094             else
1095                 paused_for_mouse = PAUSE_CLICK;
1096
1097             /* Set the pause mouse return codes to -1 */
1098             current = add_udv_by_name("MOUSE_KEY");
1099             current->udv_undef = FALSE;
1100             Ginteger(&current->udv_value,-1);
1101             current = add_udv_by_name("MOUSE_BUTTON");
1102             current->udv_undef = FALSE;
1103             Ginteger(&current->udv_value,-1);
1104         } else
1105             int_warn(NO_CARET,"Mousing not active");
1106     } else
1107 #endif
1108         sleep_time = real(const_express(&a));
1109
1110     if (!(END_OF_COMMAND)) {
1111         if (!isstring(c_token))
1112             int_error(c_token, "expecting string");
1113         else {
1114             quote_str(buf, c_token, MAX_LINE_LEN);
1115             ++c_token;
1116 #ifdef _Windows
1117             if (sleep_time >= 0)
1118 #elif defined(OS2)
1119                 if (strcmp(term->name, "pm") != 0 || sleep_time >= 0)
1120 #elif defined(MTOS)
1121                     if (strcmp(term->name, "mtos") != 0 || sleep_time >= 0)
1122 #endif /* _Windows */
1123                         fputs(buf, stderr);
1124             text = 1;
1125         }
1126     }
1127     if (sleep_time < 0) {
1128 #ifdef _Windows
1129     if (paused_for_mouse && !graphwin.hWndGraph) {
1130         if (interactive) { /* cannot wait for Enter in a non-interactive session without the graph window */
1131             char tmp[512];
1132             if (buf) fprintf(stderr,"%s\n", buf);
1133             fgets(tmp, 512, stdin); /* graphical window not yet initialized, wait for any key here */
1134         }
1135     } else { /* pausing via graphical windows */
1136         int tmp = paused_for_mouse;
1137         if (buf && paused_for_mouse) fprintf(stderr,"%s\n", buf);
1138         if (!Pause(buf)) {
1139             if (!tmp) {
1140                 free(buf);
1141                 bail_to_command_line();
1142             } else {
1143                 if (!graphwin.hWndGraph) 
1144                     bail_to_command_line();
1145             }
1146         }
1147     }
1148 #elif defined(OS2)
1149         if (strcmp(term->name, "pm") == 0 && sleep_time < 0) {
1150             int rc;
1151             if ((rc = PM_pause(buf)) == 0) {
1152                 /* if (!CallFromRexx)
1153                  * would help to stop REXX programs w/o raising an error message
1154                  * in RexxInterface() ...
1155                  */
1156                 free(buf);
1157                 bail_to_command_line();
1158             } else if (rc == 2) {
1159                 fputs(buf, stderr);
1160                 text = 1;
1161                 (void) fgets(buf, strlen(buf), stdin);
1162             }
1163         }
1164 #elif defined(_Macintosh)
1165         if (strcmp(term->name, "macintosh") == 0 && sleep_time < 0)
1166             Pause( (int)sleep_time );
1167 #elif defined(MTOS)
1168         if (strcmp(term->name, "mtos") == 0) {
1169             int MTOS_pause(char *buf);
1170             int rc;
1171             if ((rc = MTOS_pause(buf)) == 0)
1172                 free(buf);
1173             bail_to_command_line();
1174             else if (rc == 2) {
1175                 fputs(buf, stderr);
1176                 text = 1;
1177                 (void) fgets(buf, strlen(buf), stdin);
1178             }
1179         } else if (strcmp(term->name, "atari") == 0) {
1180             char *line = readline("");
1181             if (line)
1182                 free(line);
1183         } else
1184             (void) fgets(buf, strlen(buf), stdin);
1185 #elif defined(ATARI)
1186         if (strcmp(term->name, "atari") == 0) {
1187             char *line = readline("");
1188             if (line)
1189                 free(line);
1190         } else
1191             (void) fgets(buf, strlen(buf), stdin);
1192 #else /* !(_Windows || OS2 || _Macintosh || MTOS || ATARI) */
1193 #ifdef USE_MOUSE
1194         if (term && term->waitforinput) {
1195             /* term->waitforinput() will return,
1196              * if CR was hit */
1197             term->waitforinput();
1198         } else {
1199 #endif /* USE_MOUSE */
1200         (void) fgets(buf, sizeof(buf), stdin);
1201         /* Hold until CR hit. */
1202 #ifdef USE_MOUSE
1203         }
1204 #endif /* USE_MOUSE */
1205 #endif /* !(_Windows || OS2 || _Macintosh || MTOS || ATARI) */
1206     }
1207     if (sleep_time > 0)
1208         GP_SLEEP(sleep_time);
1209
1210     if (text != 0 && sleep_time >= 0)
1211         fputc('\n', stderr);
1212     screen_ok = FALSE;
1213
1214     free(buf);
1215
1216 }
1217
1218
1219 /* process the 'plot' command */
1220 void
1221 plot_command()
1222 {
1223     plot_token = c_token++;
1224     plotted_data_from_stdin = FALSE;
1225     SET_CURSOR_WAIT;
1226 #ifdef USE_MOUSE
1227     plot_mode(MODE_PLOT);
1228     add_udv_by_name("MOUSE_X")->udv_undef = TRUE;
1229     add_udv_by_name("MOUSE_Y")->udv_undef = TRUE;
1230     add_udv_by_name("MOUSE_X2")->udv_undef = TRUE;
1231     add_udv_by_name("MOUSE_Y2")->udv_undef = TRUE;
1232     add_udv_by_name("MOUSE_BUTTON")->udv_undef = TRUE;
1233     add_udv_by_name("MOUSE_SHIFT")->udv_undef = TRUE;
1234     add_udv_by_name("MOUSE_ALT")->udv_undef = TRUE;
1235     add_udv_by_name("MOUSE_CTRL")->udv_undef = TRUE;
1236 #endif
1237     plotrequest();
1238     SET_CURSOR_ARROW;
1239 }
1240
1241
1242 void
1243 print_set_output(char *name, TBOOLEAN append_p)
1244 {
1245     if (print_out && print_out != stderr && print_out != stdout) {
1246 #ifdef PIPES
1247         if (print_out_name[0] == '|') {
1248             if (0 > pclose(print_out))
1249                 perror(print_out_name);
1250         } else
1251 #endif
1252             if (0 > fclose(print_out))
1253                 perror(print_out_name);
1254     }
1255
1256     if (print_out_name)
1257         free(print_out_name);
1258
1259     print_out_name = NULL;
1260
1261     if (! name) {
1262         print_out = stderr;
1263         return;
1264     }
1265
1266     if (! strcmp(name, "-")) {
1267         print_out = stdout;
1268         return;
1269     }
1270
1271 #ifdef PIPES
1272     if (name[0]=='|') {
1273         print_out = popen(name + 1, "w");
1274         if (!print_out)
1275             perror(name);
1276         else
1277             print_out_name = name;
1278         return;
1279     }
1280 #endif
1281
1282     print_out = fopen(name, append_p ? "a" : "w");
1283     if (!print_out) {
1284         perror(name);
1285         return;
1286     }
1287
1288     print_out_name = name;
1289 }
1290
1291 char *
1292 print_show_output()
1293 {
1294     if (print_out==stdout)
1295         return "<stdout>";
1296     if (!print_out || print_out==stderr || !print_out_name)
1297         return "<stderr>";
1298     return print_out_name;
1299 }
1300
1301 /* process the 'print' command */
1302 void
1303 print_command()
1304 {
1305     struct value a;
1306     /* space printed between two expressions only */
1307     int need_space = 0;
1308
1309     if (!print_out) {
1310         print_out = stderr;
1311     }
1312     screen_ok = FALSE;
1313     do {
1314 #ifdef GP_STRING_VARS
1315         ++c_token;
1316         const_express(&a);
1317         if (a.type == STRING) {
1318             fputs(a.v.string_val, print_out);
1319             gpfree_string(&a);
1320             need_space = 0;
1321         } else {
1322             if (need_space)
1323                 putc(' ', print_out);
1324             disp_value(print_out, &a, FALSE);
1325             need_space = 1;
1326         }
1327 #else
1328         char *s;
1329         ++c_token;
1330         s = try_to_get_string();
1331         if (s) {
1332             fputs(s, print_out);
1333             free(s);
1334             need_space = 0;
1335         } else {
1336             (void) const_express(&a);
1337             if (need_space)
1338                 putc(' ', print_out);
1339             disp_value(print_out, &a, FALSE);
1340             need_space = 1;
1341         }
1342 #endif
1343     } while (!END_OF_COMMAND && equals(c_token, ","));
1344
1345     (void) putc('\n', print_out);
1346     fflush(print_out);
1347 }
1348
1349
1350 /* process the 'pwd' command */
1351 void
1352 pwd_command()
1353 {
1354     char *save_file = NULL;
1355
1356     save_file = (char *) gp_alloc(PATH_MAX, "print current dir");
1357     if (save_file) {
1358         GP_GETCWD(save_file, PATH_MAX);
1359         fprintf(stderr, "%s\n", save_file);
1360         free(save_file);
1361     }
1362     c_token++;
1363 }
1364
1365
1366 /* process the 'replot' command */
1367 void
1368 replot_command()
1369 {
1370     if (!*replot_line)
1371         int_error(c_token, "no previous plot");
1372     /* Disable replot for some reason; currently used by the mouse/hotkey
1373        capable terminals to avoid replotting when some data come from stdin,
1374        i.e. when  plotted_data_from_stdin==1  after plot "-".
1375     */
1376     if (replot_disabled) {
1377         replot_disabled = FALSE;
1378 #if 1
1379         bail_to_command_line(); /* be silent --- don't mess the screen */
1380 #else
1381         int_error(c_token, "cannot replot data coming from stdin");
1382 #endif
1383     }
1384     if (!term) /* unknown terminal */
1385         int_error(c_token, "use 'set term' to set terminal type first");
1386
1387     c_token++;
1388     SET_CURSOR_WAIT;
1389     if (term->flags & TERM_INIT_ON_REPLOT)
1390         term->init();
1391     replotrequest();
1392     SET_CURSOR_ARROW;
1393 }
1394
1395
1396 /* process the 'reread' command */
1397 void
1398 reread_command()
1399 {
1400     FILE *fp = lf_top();
1401
1402     if (fp != (FILE *) NULL)
1403         rewind(fp);
1404     c_token++;
1405 }
1406
1407
1408 /* process the 'save' command */
1409 void
1410 save_command()
1411 {
1412     FILE *fp;
1413     char *save_file = NULL;
1414     char *save_locale = NULL;
1415     int what;
1416
1417     c_token++;
1418     what = lookup_table(&save_tbl[0], c_token);
1419
1420     switch (what) {
1421         case SAVE_FUNCS:
1422         case SAVE_SET:
1423         case SAVE_TERMINAL:
1424         case SAVE_VARS:
1425             c_token++;
1426             break;
1427         default:
1428             break;
1429     }
1430
1431     save_file = try_to_get_string();
1432     if (!save_file)
1433             int_error(c_token, "expecting filename");
1434 #ifdef PIPES
1435     if (save_file[0]=='|')
1436         fp = popen(save_file+1,"w");
1437     else
1438 #endif
1439     {
1440     gp_expand_tilde(&save_file);
1441     fp = strcmp(save_file,"-") ? loadpath_fopen(save_file,"w") : stdout;
1442     }
1443
1444     if (!fp)
1445         os_error(c_token, "Cannot open save file");
1446
1447 #ifdef HAVE_LOCALE_H
1448     /* Make sure that numbers in the saved gnuplot commands use standard form */
1449     if (strcmp(localeconv()->decimal_point,".")) {
1450         save_locale = gp_strdup(setlocale(LC_NUMERIC,NULL));
1451         setlocale(LC_NUMERIC,"C");
1452     }
1453 #endif
1454
1455     switch (what) {
1456         case SAVE_FUNCS:
1457             save_functions(fp);
1458         break;
1459     case SAVE_SET:
1460             save_set(fp);
1461         break;
1462     case SAVE_TERMINAL:
1463             save_term(fp);
1464         break;
1465     case SAVE_VARS:
1466             save_variables(fp);
1467         break;
1468     default:
1469             save_all(fp);
1470     }
1471
1472 #ifdef HAVE_LOCALE_H
1473     if (save_locale) {
1474         setlocale(LC_NUMERIC,save_locale);
1475         free(save_locale);
1476         fprintf(fp, "set decimalsign locale \"%s\"\n", setlocale(LC_NUMERIC,NULL));
1477         fprintf(fp, "set decimalsign '%s'\n", decimalsign);
1478     }
1479 #endif
1480
1481     if (stdout != fp) {
1482 #ifdef PIPES
1483         if (save_file[0] == '|')
1484             (void) pclose(fp);
1485         else
1486 #endif
1487             (void) fclose(fp);
1488     }
1489
1490     free(save_file);
1491 }
1492
1493
1494 /* process the 'screendump' command */
1495 void
1496 screendump_command()
1497 {
1498     c_token++;
1499 #ifdef _Windows
1500     screen_dump();
1501 #else
1502     fputs("screendump not implemented\n", stderr);
1503 #endif
1504 }
1505
1506
1507 /* set_command() is in set.c */
1508
1509 /* 'shell' command is processed by do_shell(), see below */
1510
1511 /* show_command() is in show.c */
1512
1513
1514 /* process the 'splot' command */
1515 void
1516 splot_command()
1517 {
1518     plot_token = c_token++;
1519     plotted_data_from_stdin = FALSE;
1520     SET_CURSOR_WAIT;
1521 #ifdef USE_MOUSE
1522     plot_mode(MODE_SPLOT);
1523     add_udv_by_name("MOUSE_X")->udv_undef = TRUE;
1524     add_udv_by_name("MOUSE_Y")->udv_undef = TRUE;
1525     add_udv_by_name("MOUSE_X2")->udv_undef = TRUE;
1526     add_udv_by_name("MOUSE_Y2")->udv_undef = TRUE;
1527     add_udv_by_name("MOUSE_BUTTON")->udv_undef = TRUE;
1528 #endif
1529     plot3drequest();
1530     SET_CURSOR_ARROW;
1531 }
1532
1533
1534 /* process the 'system' command */
1535 void
1536 system_command()
1537 {
1538     char *cmd;
1539     ++c_token;
1540     cmd = try_to_get_string();
1541     do_system(cmd);
1542     free(cmd);
1543 }
1544
1545
1546 /* process the 'test palette' command
1547  *
1548  * note 1: it works on terminals supporting as well as not supporting pm3d
1549  * note 2: due to the call to load_file(), the rest of the current command
1550  *         line after 'test palette ;' is discarded
1551  */
1552 static void
1553 test_palette_subcommand()
1554 {
1555     enum {test_palette_colors = 256};
1556
1557     double gray, z[test_palette_colors];
1558     rgb_color rgb1[test_palette_colors];
1559     int i;
1560     static const char pre1[] = "\
1561 reset;set multi;\
1562 uns border;uns key;set tic out;uns xtics;uns ytics;\
1563 se cbtic 0,0.1,1;se cbtic nomirr;\
1564 se xr[0:1];se yr[0:1];se zr[0:1];se cbr[0:1];\
1565 se pm3d map;set colorbox hor user orig 0.08,0.07 size 0.79,0.12;";
1566     static const char pre2[] = "splot 1/0;\n\n\n";
1567         /* note: those \n's are because of x11 terminal problems with blocking pipe */
1568     static const char pre3[] = "\
1569 se size 1,0.8;se orig 0,0.2;uns pm3d;\
1570 se key outside;se grid;se tics in;se xtics 0,0.1;se ytics 0,0.1;\
1571 se tit'R,G,B profiles of the current color palette';";
1572     static const char post[] = "\
1573 \n\n\nuns multi;se orig 0,0;se size 1,1;\n"; /* no final 'reset' in favour of mousing */
1574     int can_pm3d = (term->make_palette && term->set_color);
1575     char *order = "rgb";
1576     char *save_replot_line;
1577     TBOOLEAN save_is_3d_plot;
1578 #ifdef WITH_IMAGE
1579     TBOOLEAN save_is_cb_plot;
1580 #endif
1581     FILE *f = tmpfile();
1582     char *save_locale = NULL;
1583
1584     c_token++;
1585     /* parse optional option */
1586     if (!END_OF_COMMAND) {
1587         int err = (token[c_token].length != 3);
1588
1589         order = gp_input_line + token[c_token].start_index;
1590         if (!err) {
1591             err += (memchr(order, 'r', 3) == NULL);
1592             err += (memchr(order, 'g', 3) == NULL);
1593             err += (memchr(order, 'b', 3) == NULL);
1594         }
1595         if (err)
1596             int_error(c_token, "combination rgb or gbr or brg etc. expected");
1597         c_token++;
1598     }
1599     if (!f)
1600         int_error(NO_CARET, "cannot write temporary file");
1601
1602 #ifdef HAVE_LOCALE_H
1603     /* Make sure that numbers in the saved gnuplot commands use standard form */
1604     if (strcmp(localeconv()->decimal_point,".")) {
1605         save_locale = gp_strdup(setlocale(LC_NUMERIC,NULL));
1606         setlocale(LC_NUMERIC,"C");
1607     }
1608 #endif
1609
1610     /* generate r,g,b curves */
1611     for (i = 0; i < test_palette_colors; i++) {
1612         /* colours equidistantly from [0,1] */
1613         z[i] = (double)i / (test_palette_colors - 1);
1614         gray = (sm_palette.positive == SMPAL_NEGATIVE) ? 1-z[i] : z[i];
1615         rgb1_from_gray(gray, &rgb1[i]);
1616     }
1617
1618     /* commands to setup the test palette plot */
1619     enable_reset_palette = 0;
1620     save_replot_line = gp_strdup(replot_line);
1621     save_is_3d_plot = is_3d_plot;
1622 #ifdef WITH_IMAGE
1623     save_is_cb_plot = is_cb_plot;
1624 #endif
1625     fputs(pre1, f);
1626     if (can_pm3d)
1627         fputs(pre2, f);
1628     fputs(pre3, f);
1629     /* put inline data of the r,g,b curves */
1630     fputs("p", f);
1631     for (i=0; i<strlen(order); i++) {
1632         if (i > 0)
1633             fputs(",", f);
1634         fputs("'-'tit'", f);
1635         switch (order[i]) {
1636         case 'r':
1637             fputs("red'w l 1", f);
1638             break;
1639         case 'g':
1640             fputs("green'w l 2", f);
1641             break;
1642         case 'b':
1643             fputs("blue'w l 3", f);
1644             break;
1645         } /* switch(order[i]) */
1646     } /* for (i) */
1647     fputs("\n", f);
1648     for (i = 0; i < 3; i++) {
1649         int k, c = order[i];
1650
1651         for (k = 0; k < test_palette_colors; k++) {
1652             double rgb = (c=='r')
1653                 ? rgb1[k].r :
1654                 ((c=='g') ? rgb1[k].g : rgb1[k].b);
1655
1656             fprintf(f, "%0.4f\t%0.4f\n", z[k], rgb);
1657         }
1658         fputs("e\n", f);
1659     }
1660     fputs(post, f);
1661
1662     /* save current gnuplot 'set' status because of the tricky sets 
1663      * for our temporary testing plot.
1664      */
1665     save_set(f);
1666
1667 #ifdef HAVE_LOCALE_H
1668     if (save_locale) {
1669         setlocale(LC_NUMERIC,save_locale);
1670         free(save_locale);
1671         fprintf(f, "set decimalsign locale \"%s\"\n", setlocale(LC_NUMERIC,NULL));
1672         fprintf(f, "set decimalsign '%s'\n", decimalsign);
1673     }
1674 #endif
1675
1676     /* execute all commands from the temporary file */
1677     rewind(f);
1678     load_file(f, NULL, FALSE); /* note: it does fclose(f) */
1679
1680     /* enable reset_palette() and restore replot line */
1681     enable_reset_palette = 1;
1682     free(replot_line);
1683     replot_line = save_replot_line;
1684     is_3d_plot = save_is_3d_plot;
1685 #ifdef WITH_IMAGE
1686     is_cb_plot = save_is_cb_plot;
1687 #endif
1688
1689     /* further, gp_input_line[] and token[] now destroyed! */
1690     c_token = num_tokens = 0;
1691 }
1692
1693
1694 /* process the undocumented 'test time' command
1695  *      test time 'format' 'string'
1696  * to assist testing of time routines
1697  */
1698 static void
1699 test_time_subcommand()
1700 {
1701     char *format = NULL;
1702     char *string = NULL;
1703     struct tm tm;
1704     double secs;
1705
1706     /* given a format and a time string, exercise the time code */
1707
1708     if (isstring(++c_token)) {
1709         m_quote_capture(&format, c_token, c_token);
1710         if (isstring(++c_token)) {
1711             m_quote_capture(&string, c_token, c_token);
1712             memset(&tm, 0, sizeof(tm));
1713             gstrptime(string, format, &tm);
1714             secs = gtimegm(&tm);
1715             fprintf(stderr, "internal = %f - %d/%d/%d::%d:%d:%d , wday=%d, yday=%d\n",
1716                     secs, tm.tm_mday, tm.tm_mon + 1, tm.tm_year % 100,
1717                     tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday,
1718                     tm.tm_yday);
1719             memset(&tm, 0, sizeof(tm));
1720             ggmtime(&tm, secs);
1721             gstrftime(string, strlen(string), format, secs);
1722             fprintf(stderr, "convert back \"%s\" - %d/%d/%d::%d:%d:%d , wday=%d, yday=%d\n",
1723                     string, tm.tm_mday, tm.tm_mon + 1, tm.tm_year % 100,
1724                     tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday,
1725                     tm.tm_yday);
1726             free(string);
1727             ++c_token;
1728         } /* else: expecting time string */
1729         free(format);
1730     } /* else: expecting format string */
1731 }
1732
1733
1734 /* process the 'test' command */
1735 void
1736 test_command()
1737 {
1738     int what;
1739     c_token++;
1740     if (END_OF_COMMAND) {
1741         test_term();
1742         return;
1743     }
1744
1745     what = lookup_table(&test_tbl[0], c_token);
1746     switch (what) {
1747         case TEST_TERMINAL: test_term(); break;
1748         case TEST_PALETTE: test_palette_subcommand(); break;
1749         case TEST_TIME: test_time_subcommand(); break;
1750         default:
1751 #if 1
1752             int_error(c_token, "none or keyword 'terminal' or 'palette' expected");
1753 #else
1754             /* don't document undocumented command :-) */
1755             int_error(c_token, "none or keyword 'terminal', 'palette' or 'time' expected");
1756 #endif
1757     }
1758 }
1759
1760
1761 /* unset_command is in unset.c */
1762
1763
1764 /* process the 'update' command */
1765 void
1766 update_command()
1767 {
1768     /* old parameter filename */
1769     char *opfname = NULL;
1770     /* new parameter filename */
1771     char *npfname = NULL;
1772
1773     c_token++;
1774     if (!(opfname = try_to_get_string()))
1775         int_error(c_token, "Parameter filename expected");
1776     if (!END_OF_COMMAND && !(npfname = try_to_get_string()))
1777         int_error(c_token, "New parameter filename expected");
1778
1779     update(opfname, npfname);
1780     free(npfname);
1781     free(opfname);
1782 }
1783
1784
1785 /* process invalid commands and, on OS/2, REXX commands */
1786 void
1787 invalid_command()
1788 {
1789 #ifdef OS2
1790    if (token[c_token].is_token) {
1791       int rc;
1792       rc = ExecuteMacro(gp_input_line + token[c_token].start_index,
1793               token[c_token].length);
1794       if (rc == 0) {
1795          c_token = num_tokens = 0;
1796          return;
1797       }
1798     }
1799 #endif
1800     int_error(c_token, "invalid command");
1801 }
1802
1803
1804 /*
1805  * Auxiliary routines
1806  */
1807
1808 /* used by changedir_command() */
1809 static int
1810 changedir(char *path)
1811 {
1812 #if defined(MSDOS) || defined(WIN16) || defined(ATARI) || defined(DOS386)
1813 # if defined(__ZTC__)
1814     unsigned dummy;             /* it's a parameter needed for dos_setdrive */
1815 # endif
1816
1817     /* first deal with drive letter */
1818
1819     if (isalpha(path[0]) && (path[1] == ':')) {
1820         int driveno = toupper(path[0]) - 'A';   /* 0=A, 1=B, ... */
1821
1822 # if defined(ATARI)
1823         (void) Dsetdrv(driveno);
1824 # endif
1825
1826 # if defined(__ZTC__)
1827         (void) dos_setdrive(driveno + 1, &dummy);
1828 # endif
1829
1830 # if (defined(MSDOS) && defined(__EMX__)) || defined(__MSC__)
1831         (void) _chdrive(driveno + 1);
1832 # endif
1833
1834
1835 /* HBB: recent versions of DJGPP also have setdisk():,
1836  * so I del'ed the special code */
1837 # if ((defined(MSDOS) || defined(_Windows)) && defined(__TURBOC__)) || defined(DJGPP)
1838         (void) setdisk(driveno);
1839 # endif
1840         path += 2;              /* move past drive letter */
1841     }
1842     /* then change to actual directory */
1843     if (*path)
1844         if (chdir(path))
1845             return 1;
1846
1847     return 0;                   /* should report error with setdrive also */
1848
1849 #elif defined(WIN32)
1850     return !(SetCurrentDirectory(path));
1851 #elif defined(__EMX__) && defined(OS2)
1852     return _chdir2(path);
1853 #else
1854     return chdir(path);
1855 #endif /* MSDOS, ATARI etc. */
1856 }
1857
1858
1859 /* used by replot_command() */
1860 void
1861 replotrequest()
1862 {
1863     if (equals(c_token, "["))
1864         int_error(c_token, "cannot set range with replot");
1865
1866     /* do not store directly into the replot_line string, until the
1867      * new plot line has been successfully plotted. This way,
1868      * if user makes a typo in a replot line, they do not have
1869      * to start from scratch. The replot_line will be committed
1870      * after do_plot has returned, whence we know all is well
1871      */
1872     if (END_OF_COMMAND) {
1873         char *rest_args = &gp_input_line[token[c_token].start_index];
1874         size_t replot_len = strlen(replot_line);
1875         size_t rest_len = strlen(rest_args);
1876
1877         /* preserve commands following 'replot ;' */
1878         /* move rest of input line to the start
1879          * necessary because of realloc() in extend_input_line() */
1880         memmove(gp_input_line,rest_args,rest_len+1);
1881         /* reallocs if necessary */
1882         while (gp_input_line_len < replot_len+rest_len+1)
1883             extend_input_line();
1884         /* move old rest args off begin of input line to
1885          * make space for replot_line */
1886         memmove(gp_input_line+replot_len,gp_input_line,rest_len+1);
1887         /* copy previous plot command to start of input line */
1888         memcpy(gp_input_line, replot_line, replot_len);
1889     } else {
1890         char *replot_args = NULL;       /* else m_capture will free it */
1891         int last_token = num_tokens - 1;
1892
1893         /* length = length of old part + length of new part + ", " + \0 */
1894         size_t newlen = strlen(replot_line) + token[last_token].start_index +
1895         token[last_token].length - token[c_token].start_index + 3;
1896
1897         m_capture(&replot_args, c_token, last_token);   /* might be empty */
1898         while (gp_input_line_len < newlen)
1899             extend_input_line();
1900         strcpy(gp_input_line, replot_line);
1901         strcat(gp_input_line, ", ");
1902         strcat(gp_input_line, replot_args);
1903         free(replot_args);
1904     }
1905     plot_token = 0;             /* whole line to be saved as replot line */
1906
1907     screen_ok = FALSE;
1908     num_tokens = scanner(&gp_input_line, &gp_input_line_len);
1909     c_token = 1;                /* skip the 'plot' part */
1910     if (is_3d_plot)
1911         plot3drequest();
1912     else
1913         plotrequest();
1914 }
1915
1916
1917 /* Is 'set view map' currently working inside 'splot' or not? Calculation of
1918  * mouse coordinates and the corresponding routines must know it, because
1919  * 'splot' can be either true 3D plot or a 2D map.
1920  * This flag is set when entering splot command and 'set view map', i.e. by
1921  * splot_map_activate(), and reset when calling splot_map_deactivate().
1922  */
1923 static int splot_map_active = 0;
1924 /* Store values reset by 'set view map' during splot, used by those two
1925  * routines below.
1926  */
1927 static float splot_map_surface_rot_x;
1928 static float splot_map_surface_rot_z;
1929 static float splot_map_surface_scale;
1930
1931 /* This routine is called at the beginning of 'splot'. It sets up some splot
1932  * parameters needed to present the 'set view map'.
1933  */
1934 void
1935 splot_map_activate()
1936 {
1937     if (splot_map_active)
1938         return;
1939     splot_map_active = 1;
1940     /* save current values */
1941     splot_map_surface_rot_x = surface_rot_x;
1942     splot_map_surface_rot_z = surface_rot_z ;
1943     splot_map_surface_scale = surface_scale;
1944     /* set new values */
1945     surface_rot_x = 180;
1946     surface_rot_z = 0;
1947     surface_scale = 1.3;
1948     axis_array[FIRST_Y_AXIS].range_flags  ^= RANGE_REVERSE;
1949     axis_array[SECOND_Y_AXIS].range_flags ^= RANGE_REVERSE;
1950         /* note: ^ is xor */
1951 }
1952
1953
1954 /* This routine is called when the current 'set view map' is no more needed,
1955  * i.e., when calling "plot" --- the reversed y-axis et al must still be
1956  * available for mousing.
1957  */
1958 void
1959 splot_map_deactivate()
1960 {
1961     if (!splot_map_active)
1962         return;
1963     splot_map_active = 0;
1964     /* restore the original values */
1965     surface_rot_x = splot_map_surface_rot_x;
1966     surface_rot_z = splot_map_surface_rot_z;
1967     surface_scale = splot_map_surface_scale;
1968     axis_array[FIRST_Y_AXIS].range_flags  ^= RANGE_REVERSE;
1969     axis_array[SECOND_Y_AXIS].range_flags ^= RANGE_REVERSE;
1970         /* note: ^ is xor */
1971 }
1972
1973
1974 /* Support for input, shell, and help for various systems */
1975
1976 #ifdef VMS
1977
1978 # include <descrip.h>
1979 # include <rmsdef.h>
1980 # include <smgdef.h>
1981 # include <smgmsg.h>
1982 # include <ssdef.h>
1983
1984 extern lib$get_input(), lib$put_output();
1985 extern smg$read_composed_line();
1986 extern sys$putmsg();
1987 extern lbr$output_help();
1988 extern lib$spawn();
1989
1990 int vms_len;
1991
1992 unsigned int status[2] = { 1, 0 };
1993
1994 static char Help[MAX_LINE_LEN+1] = "gnuplot";
1995
1996 $DESCRIPTOR(prompt_desc, PROMPT);
1997 /* temporary fix until change to variable length */
1998 struct dsc$descriptor_s line_desc =
1999 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
2000
2001 $DESCRIPTOR(help_desc, Help);
2002 $DESCRIPTOR(helpfile_desc, "GNUPLOT$HELP");
2003
2004 /* HBB 990829: confirmed this to be used on VMS, only --> moved into
2005  * the VMS-specific section */
2006 void
2007 done(int status)
2008 {
2009     term_reset();
2010     exit(status);
2011 }
2012
2013 /* please note that the vms version of read_line doesn't support variable line
2014    length (yet) */
2015
2016 static int
2017 read_line(const char *prompt)
2018 {
2019     int more, start = 0;
2020     char expand_prompt[40];
2021
2022     current_prompt = prompt;    /* HBB NEW 20040727 */
2023
2024     prompt_desc.dsc$w_length = strlen(prompt);
2025     prompt_desc.dsc$a_pointer = (char *) prompt;
2026     strcpy(expand_prompt, "_");
2027     strncat(expand_prompt, prompt, 38);
2028     do {
2029         line_desc.dsc$w_length = MAX_LINE_LEN - start;
2030         line_desc.dsc$a_pointer = &gp_input_line[start];
2031         switch (status[1] = smg$read_composed_line(&vms_vkid, &vms_ktid, &line_desc, &prompt_desc, &vms_len)) {
2032         case SMG$_EOF:
2033             done(EXIT_SUCCESS); /* ^Z isn't really an error */
2034             break;
2035         case RMS$_TNS:          /* didn't press return in time */
2036             vms_len--;          /* skip the last character */
2037             break;              /* and parse anyway */
2038         case RMS$_BES:          /* Bad Escape Sequence */
2039         case RMS$_PES:          /* Partial Escape Sequence */
2040             sys$putmsg(status);
2041             vms_len = 0;        /* ignore the line */
2042             break;
2043         case SS$_NORMAL:
2044             break;              /* everything's fine */
2045         default:
2046             done(status[1]);    /* give the error message */
2047         }
2048         start += vms_len;
2049         gp_input_line[start] = NUL;
2050         inline_num++;
2051         if (gp_input_line[start - 1] == '\\') {
2052             /* Allow for a continuation line. */
2053             prompt_desc.dsc$w_length = strlen(expand_prompt);
2054             prompt_desc.dsc$a_pointer = expand_prompt;
2055             more = 1;
2056             --start;
2057         } else {
2058             line_desc.dsc$w_length = strlen(gp_input_line);
2059             line_desc.dsc$a_pointer = gp_input_line;
2060             more = 0;
2061         }
2062     } while (more);
2063     return 0;
2064 }
2065
2066
2067 # ifdef NO_GIH
2068 void
2069 help_command()
2070 {
2071     int first = c_token;
2072
2073     while (!END_OF_COMMAND)
2074         ++c_token;
2075
2076     strcpy(Help, "GNUPLOT ");
2077     capture(Help + 8, first, c_token - 1, sizeof(Help) - 9);
2078     help_desc.dsc$w_length = strlen(Help);
2079     if ((vaxc$errno = lbr$output_help(lib$put_output, 0, &help_desc,
2080                                       &helpfile_desc, 0, lib$get_input)) != SS$_NORMAL)
2081         os_error(NO_CARET, "can't open GNUPLOT$HELP");
2082 }
2083 # endif                         /* NO_GIH */
2084
2085
2086 void
2087 do_shell()
2088 {
2089     screen_ok = FALSE;
2090     c_token++;
2091
2092     if ((vaxc$errno = lib$spawn()) != SS$_NORMAL) {
2093         os_error(NO_CARET, "spawn error");
2094     }
2095 }
2096
2097
2098 static void
2099 do_system(const char *cmd)
2100 {
2101
2102      if (!cmd)
2103         return;
2104
2105     /* gp_input_line is filled by read_line or load_file, but
2106      * line_desc length is set only by read_line; adjust now
2107      */
2108     line_desc.dsc$w_length = strlen(cmd);
2109     line_desc.dsc$a_pointer = (char *) cmd;
2110
2111     if ((vaxc$errno = lib$spawn(&line_desc)) != SS$_NORMAL)
2112         os_error(NO_CARET, "spawn error");
2113
2114     (void) putc('\n', stderr);
2115
2116 }
2117 #endif /* VMS */
2118
2119
2120 #ifdef _Windows
2121 # ifdef NO_GIH
2122 void
2123 help_command()
2124 {
2125
2126     if (END_OF_COMMAND)
2127         WinHelp(textwin.hWndParent, (LPSTR) winhelpname, HELP_INDEX, (DWORD) NULL);
2128     else {
2129         char buf[128];
2130         int start = ++c_token;
2131         while (!(END_OF_COMMAND))
2132             c_token++;
2133         capture(buf, start, c_token - 1, 128);
2134         WinHelp(textwin.hWndParent, (LPSTR) winhelpname, HELP_PARTIALKEY, (DWORD) buf);
2135     }
2136 }
2137 # endif                         /* NO_GIH */
2138 #endif /* _Windows */
2139
2140
2141 /*
2142  * help_command: (not VMS, although it would work) Give help to the user. It
2143  * parses the command line into helpbuf and supplies help for that string.
2144  * Then, if there are subtopics available for that key, it prompts the user
2145  * with this string. If more input is given, help_command is called
2146  * recursively, with argument 0.  Thus a more specific help can be supplied.
2147  * This can be done repeatedly.  If null input is given, the function returns,
2148  * effecting a backward climb up the tree.
2149  * David Kotz (David.Kotz@Dartmouth.edu) 10/89
2150  * drd - The help buffer is first cleared when called with toplevel=1.
2151  * This is to fix a bug where help is broken if ^C is pressed whilst in the
2152  * help.
2153  * Lars - The "int toplevel" argument is gone. I have converted it to a
2154  * static variable.
2155  *
2156  * FIXME - helpbuf is never free()'d
2157  */
2158
2159 #ifndef NO_GIH
2160 void
2161 help_command()
2162 {
2163     static char *helpbuf = NULL;
2164     static char *prompt = NULL;
2165     static int toplevel = 1;
2166     int base;                   /* index of first char AFTER help string */
2167     int len;                    /* length of current help string */
2168     TBOOLEAN more_help;
2169     TBOOLEAN only;              /* TRUE if only printing subtopics */
2170     TBOOLEAN subtopics;         /* 0 if no subtopics for this topic */
2171     int start;                  /* starting token of help string */
2172     char *help_ptr;             /* name of help file */
2173 # if defined(SHELFIND)
2174     static char help_fname[256] = "";   /* keep helpfilename across calls */
2175 # endif
2176
2177 # if defined(ATARI) || defined(MTOS)
2178     char const *const ext[] = { NULL };
2179 # endif
2180
2181     if ((help_ptr = getenv("GNUHELP")) == (char *) NULL)
2182 # ifndef SHELFIND
2183         /* if can't find environment variable then just use HELPFILE */
2184
2185 /* patch by David J. Liu for getting GNUHELP from home directory */
2186 #  if (defined(__TURBOC__) && (defined(MSDOS) || defined(DOS386))) || defined(__DJGPP__)
2187         help_ptr = HelpFile;
2188 #  else
2189 #   if defined(ATARI) || defined(MTOS)
2190     {
2191         /* I hope findfile really can accept a NULL argument ... */
2192         if ((help_ptr = findfile(HELPFILE, user_gnuplotpath, ext)) == NULL)
2193             help_ptr = findfile(HELPFILE, getenv("PATH"), ext);
2194         if (!help_ptr)
2195             help_ptr = HELPFILE;
2196     }
2197 #   else
2198     help_ptr = HELPFILE;
2199 #   endif                       /* ATARI || MTOS */
2200 #  endif                        /* __TURBOC__ */
2201 #ifdef OS2
2202   {
2203   /* look in the path where the executable lives */
2204   static char buf[MAXPATHLEN];
2205   char *ptr;
2206
2207   _execname(buf, sizeof(buf));
2208   _fnslashify(buf);
2209   ptr=strrchr(buf, '/');
2210   if (ptr) {
2211      *(ptr+1)='\0';
2212      strcat(buf, HELPFILE);
2213      help_ptr=&buf[0];
2214   }
2215   else
2216      help_ptr = HELPFILE;
2217   }
2218 #endif
2219 /* end of patch  - DJL */
2220
2221 # else                          /* !SHELFIND */
2222     /* try whether we can find the helpfile via shell_find. If not, just
2223        use the default. (tnx Andreas) */
2224
2225     if (!strchr(HELPFILE, ':') && !strchr(HELPFILE, '/') &&
2226         !strchr(HELPFILE, '\\')) {
2227         if (strlen(help_fname) == 0) {
2228             strcpy(help_fname, HELPFILE);
2229             if (shel_find(help_fname) == 0) {
2230                 strcpy(help_fname, HELPFILE);
2231             }
2232         }
2233         help_ptr = help_fname;
2234     } else {
2235         help_ptr = HELPFILE;
2236     }
2237 # endif                         /* !SHELFIND */
2238
2239     /* Since MSDOS DGROUP segment is being overflowed we can not allow such  */
2240     /* huge static variables (1k each). Instead we dynamically allocate them */
2241     /* on the first call to this function...                                 */
2242     if (helpbuf == NULL) {
2243         helpbuf = gp_alloc(MAX_LINE_LEN, "help buffer");
2244         prompt = gp_alloc(MAX_LINE_LEN, "help prompt");
2245         helpbuf[0] = prompt[0] = 0;
2246     }
2247     if (toplevel)
2248         helpbuf[0] = prompt[0] = 0;     /* in case user hit ^c last time */
2249
2250     /* if called recursively, toplevel == 0; toplevel must == 1 if called
2251      * from command() to get the same behaviour as before when toplevel
2252      * supplied as function argument
2253      */
2254     toplevel = 1;
2255
2256     len = base = strlen(helpbuf);
2257
2258     start = ++c_token;
2259
2260     /* find the end of the help command */
2261     while (!(END_OF_COMMAND))
2262         c_token++;
2263
2264     /* copy new help input into helpbuf */
2265     if (len > 0)
2266         helpbuf[len++] = ' ';   /* add a space */
2267     capture(helpbuf + len, start, c_token - 1, MAX_LINE_LEN - len);
2268     squash_spaces(helpbuf + base);      /* only bother with new stuff */
2269     lower_case(helpbuf + base); /* only bother with new stuff */
2270     len = strlen(helpbuf);
2271
2272     /* now, a lone ? will print subtopics only */
2273     if (strcmp(helpbuf + (base ? base + 1 : 0), "?") == 0) {
2274         /* subtopics only */
2275         subtopics = 1;
2276         only = TRUE;
2277         helpbuf[base] = NUL;    /* cut off question mark */
2278     } else {
2279         /* normal help request */
2280         subtopics = 0;
2281         only = FALSE;
2282     }
2283
2284     switch (help(helpbuf, help_ptr, &subtopics)) {
2285     case H_FOUND:{
2286             /* already printed the help info */
2287             /* subtopics now is true if there were any subtopics */
2288             screen_ok = FALSE;
2289
2290             do {
2291                 if (subtopics && !only) {
2292                     /* prompt for subtopic with current help string */
2293                     if (len > 0) {
2294                         strcpy (prompt, "Subtopic of ");
2295                         strncat (prompt, helpbuf, MAX_LINE_LEN - 16);
2296                         strcat (prompt, ": ");
2297                     } else
2298                         strcpy(prompt, "Help topic: ");
2299                     read_line(prompt);
2300                     num_tokens = scanner(&gp_input_line, &gp_input_line_len);
2301                     c_token = 0;
2302                     more_help = !(END_OF_COMMAND);
2303                     if (more_help) {
2304                         c_token--;
2305                         toplevel = 0;
2306                         /* base for next level is all of current helpbuf */
2307                         help_command();
2308                     }
2309                 } else
2310                     more_help = FALSE;
2311             } while (more_help);
2312
2313             break;
2314         }
2315     case H_NOTFOUND:
2316         printf("Sorry, no help for '%s'\n", helpbuf);
2317         break;
2318     case H_ERROR:
2319         perror(help_ptr);
2320         break;
2321     default:
2322         int_error(NO_CARET, "Impossible case in switch");
2323         break;
2324     }
2325
2326     helpbuf[base] = NUL;        /* cut it off where we started */
2327 }
2328 #endif /* !NO_GIH */
2329
2330 #ifndef VMS
2331
2332 static void
2333 do_system(const char *cmd)
2334 {
2335 # ifdef AMIGA_AC_5
2336     static char *parms[80];
2337     if (!cmd)
2338         return;
2339     getparms(input_line + 1, parms);
2340     fexecv(parms[0], parms);
2341 # elif (defined(ATARI) && defined(__GNUC__))
2342 /* || (defined(MTOS) && defined(__GNUC__)) */
2343     /* use preloaded shell, if available */
2344     short (*shell_p) (char *command);
2345     void *ssp;
2346
2347     if (!cmd)
2348         return;
2349
2350     ssp = (void *) Super(NULL);
2351     shell_p = *(short (**)(char *)) 0x4f6;
2352     Super(ssp);
2353
2354     /* this is a bit strange, but we have to have a single if */
2355     if (shell_p)
2356         (*shell_p) (cmd);
2357     else
2358         system(cmd);
2359 # elif defined(_Windows) && defined(USE_OWN_WINSYSTEM_FUNCTION)
2360     if (!cmd)
2361         return;
2362     winsystem(cmd);
2363 # else /* !(AMIGA_AC_5 || ATARI && __GNUC__ || _Windows) */
2364 /* (am, 19980929)
2365  * OS/2 related note: cmd.exe returns 255 if called w/o argument.
2366  * i.e. calling a shell by "!" will always end with an error message.
2367  * A workaround has to include checking for EMX,OS/2, two environment
2368  *  variables,...
2369  */
2370     if (!cmd)
2371         return;
2372     system(cmd);
2373 # endif /* !(AMIGA_AC_5 || ATARI&&__GNUC__ || _Windows) */
2374 }
2375
2376
2377 # ifdef AMIGA_AC_5
2378 /******************************************************************************
2379  * Parses the command string (for fexecv use) and  converts the first token
2380  * to lower case
2381  *****************************************************************************/
2382 static void
2383 getparms(char *command, char **parms)
2384 {
2385     static char strg0[256];
2386     int i = 0, j = 0, k = 0;            /* A bunch of indices */
2387
2388     while (command[j] != NUL) { /* Loop on string characters */
2389         parms[k++] = strg0 + i;
2390         while (command[j] == ' ')
2391             ++j;
2392         while (command[j] != ' ' && command[j] != NUL) {
2393             if (command[j] == '"') {    /* Get quoted string */
2394                 do {
2395                     strg0[i++] = command[j++];
2396                 } while (command[j] != '"' && command[j] != NUL);
2397             }
2398             strg0[i++] = command[j++];
2399         }
2400         if (strg0[i] != NUL)
2401             strg0[i++] = NUL;   /* NUL terminate every token */
2402     }
2403     parms[k] = NUL;
2404
2405     /* Convert to lower case */
2406     /* FIXME HBB 20010621: do we really want to stop on char *before*
2407      * the actual end of the string strg0[]? */
2408     for (k=0; strg0[k+1] != NUL; k++)
2409         if (strg0[k] >= 'A' && (strg0[k] <= 'Z'))
2410             strg0[k] += ('a' - 'A');
2411 }
2412
2413 # endif                         /* AMIGA_AC_5 */
2414
2415
2416 # if defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
2417 /* keep some compilers happy */
2418 static char *rlgets __PROTO((char *s, size_t n, const char *prompt));
2419
2420 static char *
2421 rlgets(char *s, size_t n, const char *prompt)
2422 {
2423     static char *line = (char *) NULL;
2424     static int leftover = -1;   /* index of 1st char leftover from last call */
2425
2426     if (leftover == -1) {
2427         /* If we already have a line, first free it */
2428         if (line != (char *) NULL) {
2429             free(line);
2430             line = NULL;
2431             /* so that ^C or int_error during readline() does
2432              * not result in line being free-ed twice */
2433         }
2434         line = readline_ipc((interactive) ? prompt : "");
2435         leftover = 0;
2436         /* If it's not an EOF */
2437         if (line && *line) {
2438 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
2439             int found;
2440             /* Initialize readline history functions */
2441             using_history();
2442
2443             /* search in the history for entries containing line.
2444              * They may have other tokens before and after line, hence
2445              * the check on strcmp below. */
2446             found = history_search(line, -1);
2447             if (found != -1 && !strcmp(current_history()->line,line)) {
2448             /* this line is already in the history, remove the earlier entry */
2449 #if defined(HAVE_LIBREADLINE)
2450                 HIST_ENTRY *removed = remove_history(where_history());
2451                 /* according to history docs we are supposed to free the stuff */
2452                 if (removed->line) free(removed->line);
2453                 if (removed->data) free(removed->data);
2454                 free(removed);
2455 #else
2456                 remove_history(where_history());
2457 #endif /* !HAVE_LIBREADLINE */
2458             }
2459             add_history(line);
2460 #  else /* !HAVE_LIBREADLINE && !HAVE_LIBEDITLINE */
2461             add_history(line);
2462 #  endif
2463         }
2464     }
2465     if (line) {
2466         /* s will be NUL-terminated here */
2467         safe_strncpy(s, line + leftover, n);
2468         leftover += strlen(s);
2469         if (line[leftover] == NUL)
2470             leftover = -1;
2471         return s;
2472     }
2473     return NULL;
2474 }
2475 # endif                         /* READLINE || HAVE_LIBREADLINE */
2476
2477
2478 # if defined(MSDOS) || defined(_Windows) || defined(DOS386)
2479 void
2480 do_shell()
2481 {
2482     screen_ok = FALSE;
2483     c_token++;
2484
2485     if (user_shell) {
2486 #  if defined(_Windows)
2487         if (WinExec(user_shell, SW_SHOWNORMAL) <= 32)
2488 #  elif defined(DJGPP)
2489             if (system(user_shell) == -1)
2490 #  else
2491                 if (spawnl(P_WAIT, user_shell, NULL) == -1)
2492 #  endif                        /* !(_Windows || DJGPP) */
2493                     os_error(NO_CARET, "unable to spawn shell");
2494     }
2495 }
2496
2497 # elif defined(AMIGA_SC_6_1)
2498
2499 void
2500 do_shell()
2501 {
2502     screen_ok = FALSE;
2503     c_token++;
2504
2505     if (user_shell) {
2506         if (system(user_shell))
2507             os_error(NO_CARET, "system() failed");
2508     }
2509     (void) putc('\n', stderr);
2510 }
2511
2512 #  elif defined(OS2)
2513
2514 void
2515 do_shell()
2516 {
2517     screen_ok = FALSE;
2518     c_token++;
2519
2520     if (user_shell) {
2521         if (system(user_shell) == -1)
2522             os_error(NO_CARET, "system() failed");
2523
2524     }
2525     (void) putc('\n', stderr);
2526 }
2527
2528 #  else                         /* !OS2 */
2529
2530 /* plain old Unix */
2531
2532 #define EXEC "exec "
2533 void
2534 do_shell()
2535 {
2536     static char exec[100] = EXEC;
2537
2538     screen_ok = FALSE;
2539     c_token++;
2540
2541     if (user_shell) {
2542         if (system(safe_strncpy(&exec[sizeof(EXEC) - 1], user_shell,
2543                                 sizeof(exec) - sizeof(EXEC) - 1)))
2544             os_error(NO_CARET, "system() failed");
2545     }
2546     (void) putc('\n', stderr);
2547 }
2548
2549 # endif                         /* !MSDOS */
2550
2551 /* read from stdin, everything except VMS */
2552
2553 # if !defined(READLINE) && !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDITLINE)
2554 #  if (defined(MSDOS) || defined(DOS386)) && !defined(_Windows) && !defined(__EMX__) && !defined(DJGPP)
2555
2556 /* if interactive use console IO so CED will work */
2557
2558 #define PUT_STRING(s) cputs(s)
2559 #define GET_STRING(s,l) ((interactive) ? cgets_emu(s,l) : fgets(s,l,stdin))
2560
2561 #   ifdef __TURBOC__
2562 /* cgets implemented using dos functions */
2563 /* Maurice Castro 22/5/91 */
2564 static char *doscgets __PROTO((char *));
2565
2566 static char *
2567 doscgets(char *s)
2568 {
2569     long datseg;
2570
2571     /* protect and preserve segments - call dos to do the dirty work */
2572     datseg = _DS;
2573
2574     _DX = FP_OFF(s);
2575     _DS = FP_SEG(s);
2576     _AH = 0x0A;
2577     geninterrupt(33);
2578     _DS = datseg;
2579
2580     /* check for a carriage return and then clobber it with a null */
2581     if (s[s[1] + 2] == '\r')
2582         s[s[1] + 2] = 0;
2583
2584     /* return the input string */
2585     return (&(s[2]));
2586 }
2587 #   endif                       /* __TURBOC__ */
2588
2589 #   ifdef __ZTC__
2590 void
2591 cputs(char *s)
2592 {
2593     int i = 0;
2594     while (s[i] != NUL)
2595         bdos(0x02, s[i++], NULL);
2596 }
2597
2598 char *
2599 cgets(char *s)
2600 {
2601     bdosx(0x0A, s, NULL);
2602
2603     if (s[s[1] + 2] == '\r')
2604         s[s[1] + 2] = 0;
2605
2606     /* return the input string */
2607     return (&(s[2]));
2608 }
2609 #   endif                       /* __ZTC__ */
2610
2611 /* emulate a fgets like input function with DOS cgets */
2612 char *
2613 cgets_emu(char *str, int len)
2614 {
2615     static char buffer[128] = "";
2616     static int leftover = 0;
2617
2618     if (buffer[leftover] == NUL) {
2619         buffer[0] = 126;
2620 #   ifdef __TURBOC__
2621         doscgets(buffer);
2622 #   else
2623         cgets(buffer);
2624 #   endif
2625         fputc('\n', stderr);
2626         if (buffer[2] == 26)
2627             return NULL;
2628         leftover = 2;
2629     }
2630     safe_strncpy(str, buffer + leftover, len);
2631     leftover += strlen(str);
2632     return str;
2633 }
2634 #  else                         /* !plain DOS */
2635
2636 #   define PUT_STRING(s) fputs(s, stderr)
2637 #   define GET_STRING(s,l) fgets(s, l, stdin)
2638
2639 #  endif                        /* !plain DOS */
2640 # endif                         /* !READLINE && !HAVE_LIBREADLINE) */
2641
2642 /* this function is called for non-interactive operation. Its usage is
2643  * like fgets(), but additionally it checks for ipc events from the
2644  * terminals waitforinput() (if USE_MOUSE, and terminal is
2645  * mouseable). This function will be used when reading from a pipe.
2646  * fgets() reads in at most one less than size characters from stream and
2647  * stores them into the buffer pointed to by s.
2648  * Reading stops after an EOF or a newline.  If a newline is read, it is
2649  * stored into the buffer.  A '\0' is stored  after the last character in
2650  * the buffer. */
2651 static char*
2652 fgets_ipc(
2653     char *dest,                 /* string to fill */
2654     int len)                    /* size of it */
2655 {
2656 #ifdef USE_MOUSE
2657     if (term && term->waitforinput) {
2658         /* This a mouseable terminal --- must expect input from it */
2659         int c;                  /* char got from waitforinput() */
2660         size_t i=0;             /* position inside dest */
2661
2662         dest[0] = '\0';
2663         for (i=0; i < len-1; i++) {
2664             c = term->waitforinput();
2665             if ('\n' == c) {
2666                 dest[i] = '\n';
2667                 i++;
2668                 break;
2669             } else if (EOF == c) {
2670                 dest[i] = '\0';
2671                 return (char*) 0;
2672             } else {
2673                 dest[i] = c;
2674             }
2675         }
2676         dest[i] = '\0';
2677         return dest;
2678     } else
2679 #endif
2680         return fgets(dest, len, stdin);
2681 }
2682
2683 /* Non-VMS version */
2684 static int
2685 read_line(const char *prompt)
2686 {
2687     int start = 0;
2688     TBOOLEAN more = FALSE;
2689     int last = 0;
2690
2691     current_prompt = prompt;    /* HBB NEW 20040727 */
2692
2693 # if !defined(READLINE) && !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDITLINE)
2694     if (interactive)
2695         PUT_STRING(prompt);
2696 # endif                         /* no READLINE */
2697
2698     do {
2699         /* grab some input */
2700 # if defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
2701         if (((interactive)
2702              ? rlgets(gp_input_line + start, gp_input_line_len - start,
2703                      ((more) ? "> " : prompt))
2704              : fgets_ipc(gp_input_line + start, gp_input_line_len - start)
2705             ) == (char *) NULL)
2706 # else /* !(READLINE || HAVE_LIBREADLINE) */
2707         if (GET_STRING(gp_input_line + start, gp_input_line_len - start)
2708             == (char *) NULL)
2709 # endif /* !(READLINE || HAVE_LIBREADLINE) */
2710         {
2711             /* end-of-file */
2712             if (interactive)
2713                 (void) putc('\n', stderr);
2714             gp_input_line[start] = NUL;
2715             inline_num++;
2716             if (start > 0)      /* don't quit yet - process what we have */
2717                 more = FALSE;
2718             else
2719                 return (1);     /* exit gnuplot */
2720         } else {
2721             /* normal line input */
2722             /* gp_input_line must be NUL-terminated for strlen not to pass the
2723              * the bounds of this array */
2724             last = strlen(gp_input_line) - 1;
2725             if (last >= 0) {
2726                 if (gp_input_line[last] == '\n') {      /* remove any newline */
2727                     gp_input_line[last] = NUL;
2728                     /* Watch out that we don't backup beyond 0 (1-1-1) */
2729                     if (last > 0)
2730                         --last;
2731                 } else if (last + 2 >= gp_input_line_len) {
2732                     extend_input_line();
2733                     /* read rest of line, don't print "> " */
2734                     start = last + 1;
2735                     more = TRUE;
2736                     continue;
2737                     /* else fall through to continuation handling */
2738                 } /* if(grow buffer?) */
2739                 if (gp_input_line[last] == '\\') {
2740                     /* line continuation */
2741                     start = last;
2742                     more = TRUE;
2743                 } else
2744                     more = FALSE;
2745             } else
2746                 more = FALSE;
2747         }
2748 # if !defined(READLINE) && !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDITLINE)
2749         if (more && interactive)
2750             PUT_STRING("> ");
2751 # endif
2752     } while (more);
2753     return (0);
2754 }
2755
2756 #endif /* !VMS */
2757
2758 #if defined(_Windows)
2759 # if defined(USE_OWN_WINSYSTEM_FUNCTION)
2760 /* there is a system like call on MS Windows but it is a bit difficult to
2761    use, so we will invoke the command interpreter and use it to execute the
2762    commands */
2763 static int
2764 winsystem(const char *s)
2765 {
2766     LPSTR comspec;
2767     LPSTR execstr;
2768     LPCSTR p;
2769
2770     /* get COMSPEC environment variable */
2771 #  ifdef WIN32
2772     char envbuf[81];
2773     GetEnvironmentVariable("COMSPEC", envbuf, 80);
2774     if (*envbuf == NUL)
2775         comspec = "\\command.com";
2776     else
2777         comspec = envbuf;
2778 #  else
2779     p = GetDOSEnvironment();
2780     comspec = "\\command.com";
2781     while (*p) {
2782         if (!strncmp(p, "COMSPEC=", 8)) {
2783             comspec = p + 8;
2784             break;
2785         }
2786         p += strlen(p) + 1;
2787     }
2788 #  endif
2789     /* if the command is blank we must use command.com */
2790     p = s;
2791     while ((*p == ' ') || (*p == '\n') || (*p == '\r'))
2792         p++;
2793     if (*p == NUL) {
2794         WinExec(comspec, SW_SHOWNORMAL);
2795     } else {
2796         /* attempt to run the windows/dos program via windows */
2797         if (WinExec(s, SW_SHOWNORMAL) <= 32) {
2798             /* attempt to run it as a dos program from command line */
2799             execstr = gp_alloc(strlen(s) + strlen(comspec) + 6,
2800                                "winsystem cmdline");
2801             strcpy(execstr, comspec);
2802             strcat(execstr, " /c ");
2803             strcat(execstr, s);
2804             WinExec(execstr, SW_SHOWNORMAL);
2805             free(execstr);
2806         }
2807     }
2808
2809     /* regardless of the reality return OK - the consequences of */
2810     /* failure include shutting down Windows */
2811     return (0);                 /* success */
2812 }
2813 # endif /* USE_OWN_WINSYSTEM_FUNCTION */
2814
2815 void
2816 call_kill_pending_Pause_dialog()
2817 {
2818     kill_pending_Pause_dialog();
2819 }
2820 #endif /* _Windows */
2821
2822 #ifdef GP_MACROS
2823 /*
2824  * Walk through the input line looking for string variables preceded by @.
2825  * Replace the characters @<varname> with the contents of the string.
2826  * Anything inside quotes is not expanded.
2827  */
2828
2829 #define COPY_CHAR gp_input_line[o++] = *c; \
2830                   after_backslash = FALSE;
2831 static int
2832 string_expand()
2833 {
2834     TBOOLEAN in_squote = FALSE;
2835     TBOOLEAN in_dquote = FALSE;
2836     TBOOLEAN after_backslash = FALSE;
2837     TBOOLEAN in_comment= FALSE;
2838     int   len;
2839     int   o = 0;
2840     int   nfound = 0;
2841     char *c;
2842     char *temp_string;
2843     char  temp_char;
2844     char *m;
2845     struct udvt_entry *udv;
2846
2847     /* Most lines have no macros */
2848     if (!strchr(gp_input_line,'@'))
2849         return(0);
2850
2851     temp_string = gp_alloc(gp_input_line_len,"string variable");
2852     len = strlen(gp_input_line);
2853     if (len >= gp_input_line_len) len = gp_input_line_len-1;
2854     strncpy(temp_string,gp_input_line,len);
2855     temp_string[len] = '\0';
2856
2857     for (c=temp_string; len && c && *c; c++, len--) {
2858         switch (*c) {
2859         case '@':       /* The only tricky bit */
2860                 if (!in_squote && !in_dquote && !in_comment && isalpha(c[1])) {
2861                     /* Isolate the udv key as a null-terminated substring */
2862                     m = ++c;
2863                     while (isalnum(*c) || (*c=='_')) c++;
2864                     temp_char = *c; *c = '\0';
2865                     /* Look up the key and restore the original following char */
2866                     udv = add_udv_by_name(m);
2867                     if (udv && udv->udv_value.type == STRING) {
2868                         nfound++;
2869                         m = udv->udv_value.v.string_val;
2870                         FPRINTF((stderr,"Replacing @%s with \"%s\"\n",udv->udv_name,m));
2871                         while (strlen(m) + o + len > gp_input_line_len)
2872                             extend_input_line();
2873                         while (*m)
2874                             gp_input_line[o++] = (*m++);
2875                     } else {
2876                         int_warn( NO_CARET, "%s is not a string variable",m);
2877                     }
2878                     *c-- = temp_char;
2879                 } else
2880                     COPY_CHAR;
2881                 break;
2882
2883         case '"':       
2884                 if (!after_backslash)
2885                     in_dquote = !in_dquote;
2886                 COPY_CHAR; break;
2887         case '\'':      
2888                 in_squote = !in_squote;
2889                 COPY_CHAR; break;
2890         case '\\':
2891                 if (in_dquote)
2892                     after_backslash = !after_backslash;
2893                 gp_input_line[o++] = *c; break;
2894         case '#':
2895                 if (!in_squote && !in_dquote)
2896                     in_comment = TRUE;
2897         default :       
2898                 COPY_CHAR; break;
2899         }
2900     }
2901     gp_input_line[o] = '\0';
2902     free(temp_string);
2903
2904     if (nfound)
2905         FPRINTF((stderr,
2906                  "After string substitution command line is:\n\t%s\n",
2907                  gp_input_line));
2908
2909     return(nfound);
2910 }
2911 #endif
2912
2913 /* much more than what can be useful */
2914 #define MAX_TOTAL_LINE_LEN (1024 * MAX_LINE_LEN)
2915
2916 int
2917 do_system_func(const char *cmd, char **output)
2918 {
2919
2920 #if defined(VMS) || defined(PIPES) || (defined(ATARI) || defined(MTOS)) && defined(__PUREC__)
2921     int c;
2922     FILE *f;
2923     size_t cmd_len;
2924     int result_allocated, result_pos;
2925     char* result;
2926     int ierr = 0;
2927 # ifdef AMIGA_AC_5
2928     int fd;
2929 # elif (defined(ATARI) || defined(MTOS)) && defined(__PUREC__)
2930     char *atari_tmpfile, *atari_cmd;
2931 # elif defined(VMS)
2932     int chan, one = 1;
2933     struct dsc$descriptor_s pgmdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2934     static $DESCRIPTOR(lognamedsc, "PLOT$MAILBOX");
2935 # endif /* VMS */
2936
2937     cmd_len = strlen(cmd);
2938
2939     /* open stream */
2940 # ifdef VMS
2941     pgmdsc.dsc$a_pointer = cmd;
2942     pgmdsc.dsc$w_length = cmd_len;
2943     if (!((vaxc$errno = sys$crembx(0, &chan, 0, 0, 0, 0, &lognamedsc)) & 1))
2944         os_error(NO_CARET, "sys$crembx failed");
2945
2946     if (!((vaxc$errno = lib$spawn(&pgmdsc, 0, &lognamedsc, &one)) & 1))
2947         os_error(NO_CARET, "lib$spawn failed");
2948
2949     if ((f = fopen("PLOT$MAILBOX", "r")) == NULL)
2950         os_error(NO_CARET, "mailbox open failed");
2951 # elif (defined(ATARI) || defined(MTOS)) && defined(__PUREC__)
2952     if (system(NULL) == 0)
2953         os_error(NO_CARET, "no command shell");
2954     atari_tmpfile = tmpnam(NULL);
2955     atari_cmd = gp_alloc(cmd_len + 5 + strlen(atari_tmpfile),
2956                          "command string");
2957     strcpy(atari_cmd, cmd);
2958     strcat(atari_cmd, " >> ");
2959     strcat(atari_cmd, atari_tmpfile);
2960     system(atari_cmd);
2961     free(atari_cmd);
2962     if ((f = fopen(atari_tmpfile, "r")) == NULL)
2963 # elif defined(AMIGA_AC_5)
2964         if ((fd = open(cmd, "O_RDONLY")) == -1)
2965 # else  /* everyone else */
2966             if ((f = popen(cmd, "r")) == NULL)
2967                 os_error(NO_CARET, "popen failed");
2968 # endif /* everyone else */
2969
2970     /* get output */
2971     result_pos = 0;
2972     result_allocated = MAX_LINE_LEN;
2973     result = gp_alloc(MAX_LINE_LEN, "do_system_func");
2974     result[0] = NUL;
2975     while (1) {
2976 # if defined(AMIGA_AC_5)
2977         char ch;
2978         if (read(fd, &ch, 1) != 1)
2979             break;
2980         c = ch;
2981 # else
2982         if ((c = getc(f)) == EOF)
2983             break;
2984 # endif                         /* !AMIGA_AC_5 */
2985         /* result <- c */
2986         result[result_pos++] = c;
2987         if ( result_pos == result_allocated ) {
2988             if ( result_pos >= MAX_TOTAL_LINE_LEN ) {
2989                 result_pos--;
2990                 int_warn(NO_CARET,
2991                          "*very* long system call output has been truncated");
2992                 break;
2993             } else {
2994                 result = gp_realloc(result, result_allocated + MAX_LINE_LEN,
2995                                     "extend in do_system_func");
2996                 result_allocated += MAX_LINE_LEN;
2997             }
2998         }
2999     }
3000     result[result_pos] = NUL;
3001
3002     /* close stream */
3003 # ifdef AMIGA_AC_5
3004     (void) close(fd);
3005 # elif (defined(ATARI) || defined(MTOS)) && defined(__PUREC__)
3006     (void) fclose(f);
3007     (void) unlink(atari_tmpfile);
3008 # else                          /* Rest of the world */
3009     ierr = pclose(f);
3010 # endif
3011
3012     result = gp_realloc(result, strlen(result)+1, "do_system_func");
3013     *output = result;
3014     return ierr;
3015
3016 #else /* VMS || PIPES || ATARI && PUREC */
3017
3018     int_warn(NO_CARET, "system evaluation not supported by %s", OS);
3019     *output = gp_strdup("");
3020     return 0;
3021
3022 #endif /* VMS || PIPES || ATARI && PUREC */
3023
3024 }