#ifndef lint static char *RCSid() { return RCSid("$Id: command.c,v 1.181 2009/02/03 22:26:19 mikulik Exp $"); } #endif /* GNUPLOT - command.c */ /*[ * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley * * Permission to use, copy, and distribute this software and its * documentation for any purpose with or without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. * * Permission to modify the software is granted, but not the right to * distribute the complete modified source code. Modifications are to * be distributed as patches to the released version. Permission to * distribute binaries produced by compiling modified sources is granted, * provided you * 1. distribute the corresponding source modifications from the * released version in the form of a patch file along with the binaries, * 2. add special version identification to distinguish your version * in addition to the base release version number, * 3. provide your name and address as the primary contact for the * support of your modified version, and * 4. retain our contact information in regard to use of the base * software. * Permission to distribute the released version of the source code along * with corresponding source modifications in the form of a patch file is * granted with same provisions 2 through 4 for binary distributions. * * This software is provided "as is" without express or implied warranty * to the extent permitted by applicable law. ]*/ /* * Changes: * * Feb 5, 1992 Jack Veenstra (veenstra@cs.rochester.edu) Added support to * filter data values read from a file through a user-defined function before * plotting. The keyword "thru" was added to the "plot" command. Example * syntax: f(x) = x / 100 plot "test.data" thru f(x) This example divides all * the y values by 100 before plotting. The filter function processes the * data before any log-scaling occurs. This capability should be generalized * to filter x values as well and a similar feature should be added to the * "splot" command. * * 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu) * Added user-specified bases for log scaling. * * April 1999 Franz Bakan (bakan@ukezyk.desy.de) * Added code to support mouse-input from OS/2 PM window * Changes marked by USE_MOUSE * * May 1999, update by Petr Mikulik * Use gnuplot's pid in shared mem name * * August 1999 Franz Bakan and Petr Mikulik * Encapsulating read_line into a thread, acting on input when thread or * gnupmdrv posts an event semaphore. Thus mousing works even when gnuplot * is used as a plotting device (commands passed via pipe). * */ #include "command.h" #include "axis.h" #include "alloc.h" #include "eval.h" #include "fit.h" #include "binary.h" #include "datafile.h" #include "getcolor.h" #include "gp_hist.h" #include "gp_time.h" #include "misc.h" #include "parse.h" #include "plot.h" #include "plot2d.h" #include "plot3d.h" #include "readline.h" #include "save.h" #include "scanner.h" #include "setshow.h" #include "tables.h" #include "term_api.h" #include "util.h" #ifdef USE_MOUSE # include "mouse.h" #endif #define PROMPT "gnuplot> " #if (defined(MSDOS) || defined(DOS386)) && defined(__TURBOC__) && !defined(_Windows) unsigned _stklen = 16394; /* increase stack size */ #endif /* MSDOS && TURBOC */ #ifdef OS2_IPC # define INCL_DOSMEMMGR # define INCL_DOSPROCESS # define INCL_DOSSEMAPHORES # include PVOID input_from_PM_Terminal = NULL; char mouseSharedMemName[40] = ""; HEV semInputReady = 0; /* semaphore to be created in plot.c */ int thread_rl_Running = 0; /* running status */ int thread_rl_RetCode = -1; /* return code from readline in a thread */ #endif /* OS2_IPC */ #ifndef _Windows # include "help.h" #else # ifdef USE_OWN_WINSYSTEM_FUNCTION static int winsystem __PROTO((const char *)); # endif #endif /* _Windows */ #ifdef _Windows # include # ifdef __MSC__ # include # include /* getcwd() */ # else # include # ifndef __WATCOMC__ # include /* setdisk() */ # endif # endif /* !MSC */ # include "win/winmain.h" #endif /* _Windows */ #ifdef VMS int vms_vkid; /* Virtual keyboard id */ int vms_ktid; /* key table id, for translating keystrokes */ #endif /* VMS */ /* static prototypes */ static void command __PROTO((void)); static int changedir __PROTO((char *path)); static char* fgets_ipc __PROTO((char* dest, int len)); static char* gp_get_string __PROTO((char *, size_t, const char *)); static int read_line __PROTO((const char *prompt)); static void do_system __PROTO((const char *)); static void test_palette_subcommand __PROTO((void)); static void test_time_subcommand __PROTO((void)); #ifdef AMIGA_AC_5 static void getparms __PROTO((char *, char **)); #endif #ifdef GP_MACROS static int string_expand __PROTO((void)); TBOOLEAN expand_macros = FALSE; #endif struct lexical_unit *token; int token_table_size; char *gp_input_line; size_t gp_input_line_len; int inline_num; /* input line number */ struct udft_entry *dummy_func; /* support for replot command */ char *replot_line; int plot_token; /* start of 'plot' command */ /* flag to disable `replot` when some data are sent through stdin; * used by mouse/hotkey capable terminals */ TBOOLEAN replot_disabled = FALSE; #ifdef USE_MOUSE int paused_for_mouse = 0; #endif /* output file for the print command */ FILE *print_out = NULL; char *print_out_name = NULL; /* input data, parsing variables */ #ifdef AMIGA_SC_6_1 __far int num_tokens, c_token; #else int num_tokens, c_token; #endif static int if_depth = 0; static TBOOLEAN if_condition = FALSE; static int eval_depth = 0; static int command_exit_status = 0; /* support for dynamic size of input line */ void extend_input_line() { if (gp_input_line_len == 0) { /* first time */ gp_input_line = gp_alloc(MAX_LINE_LEN, "gp_input_line"); gp_input_line_len = MAX_LINE_LEN; gp_input_line[0] = NUL; #ifdef OS2_IPC sprintf( mouseSharedMemName, "\\SHAREMEM\\GP%i_Mouse_Input", getpid() ); if (DosAllocSharedMem((PVOID) & input_from_PM_Terminal, mouseSharedMemName, MAX_LINE_LEN, PAG_WRITE | PAG_COMMIT)) fputs("command.c: DosAllocSharedMem ERROR\n",stderr); #endif /* OS2_IPC */ } else { gp_input_line = gp_realloc(gp_input_line, gp_input_line_len + MAX_LINE_LEN, "extend input line"); gp_input_line_len += MAX_LINE_LEN; FPRINTF((stderr, "extending input line to %d chars\n", gp_input_line_len)); } } /* constant by which token table grows */ #define MAX_TOKENS 400 void extend_token_table() { if (token_table_size == 0) { /* first time */ token = (struct lexical_unit *) gp_alloc(MAX_TOKENS * sizeof(struct lexical_unit), "token table"); token_table_size = MAX_TOKENS; /* HBB: for checker-runs: */ memset(token, 0, MAX_TOKENS * sizeof(*token)); } else { token = gp_realloc(token, (token_table_size + MAX_TOKENS) * sizeof(struct lexical_unit), "extend token table"); memset(token+token_table_size, 0, MAX_TOKENS * sizeof(*token)); token_table_size += MAX_TOKENS; FPRINTF((stderr, "extending token table to %d elements\n", token_table_size)); } } #ifdef OS2_IPC void thread_read_line() { thread_rl_Running = 1; thread_rl_RetCode = ( read_line(PROMPT) ); thread_rl_Running = 0; DosPostEventSem(semInputReady); } #endif /* OS2_IPC */ int com_line() { #ifdef OS2_IPC static char *input_line_SharedMem = NULL; if (input_line_SharedMem == NULL) { /* get shared mem only once */ if (DosGetNamedSharedMem((PVOID) &input_line_SharedMem, mouseSharedMemName, PAG_WRITE | PAG_READ)) fputs("readline.c: DosGetNamedSharedMem ERROR\n", stderr); else *input_line_SharedMem = 0; } #endif /* OS2_IPC */ if (multiplot) { /* calls int_error() if it is not happy */ term_check_multiplot_okay(interactive); if (read_line("multiplot> ")) return (1); } else { #ifndef USE_MOUSE if (read_line(PROMPT)) return (1); #else # ifdef OS2_IPC ULONG u; if (thread_rl_Running == 0) { int res = _beginthread(thread_read_line,NULL,32768,NULL); if (res == -1) fputs("error command.c could not begin thread\n",stderr); } /* wait until a line is read or gnupmdrv makes shared mem available */ DosWaitEventSem(semInputReady,SEM_INDEFINITE_WAIT); DosResetEventSem(semInputReady,&u); if (thread_rl_Running) { if (input_line_SharedMem == NULL || !*input_line_SharedMem) return (0); if (*input_line_SharedMem=='%') { do_event( (struct gp_event_t*)(input_line_SharedMem+1) ); /* pass terminal's event */ input_line_SharedMem[0] = 0; /* discard the whole command line */ thread_rl_RetCode = 0; return (0); } if (*input_line_SharedMem && strstr(input_line_SharedMem,"plot") != NULL && (strcmp(term->name,"pm") && strcmp(term->name,"x11"))) { /* avoid plotting if terminal is not PM or X11 */ fprintf(stderr,"\n\tCommand(s) ignored for other than PM and X11 terminals\a\n"); if (interactive) fputs(PROMPT,stderr); input_line_SharedMem[0] = 0; /* discard the whole command line */ return (0); } # if 0 fprintf(stderr,"shared mem received: |%s|\n",input_line_SharedMem); if (*input_line_SharedMem && input_line_SharedMem[strlen(input_line_SharedMem)-1] != '\n') fprintf(stderr,"\n"); # endif strcpy(gp_input_line, input_line_SharedMem); input_line_SharedMem[0] = 0; thread_rl_RetCode = 0; } if (thread_rl_RetCode) return (1); # else /* OS2_IPC */ if (read_line(PROMPT)) return (1); # endif /* OS2_IPC */ #endif /* USE_MOUSE */ } /* So we can flag any new output: if false at time of error, * we reprint the command line before printing caret. * TRUE for interactive terminals, since the command line is typed. * FALSE for non-terminal stdin, so command line is printed anyway. * (DFK 11/89) */ screen_ok = interactive; if (do_line()) return (1); else return (0); } int do_line() { /* Line continuation has already been handled * by read_line() */ char *inlptr = gp_input_line; /* Skip leading whitespace */ while (isspace((unsigned char) *inlptr)) inlptr++; if (inlptr != gp_input_line) { /* If there was leading whitespace, copy the actual * command string to the front. use memmove() because * source and target may overlap */ memmove(gp_input_line, inlptr, strlen(inlptr)); /* Terminate resulting string */ gp_input_line[strlen(inlptr)] = NUL; } FPRINTF((stderr, "Input line: \"%s\"\n", gp_input_line)); #ifdef GP_MACROS /* Expand any string variables in the current input line. * Allow up to 4 levels of recursion */ if (expand_macros) if (string_expand() && string_expand() && string_expand() && string_expand() && string_expand()) int_error(NO_CARET, "Too many levels of nested macros"); #endif /* also used in load_file */ if (is_system(gp_input_line[0])) { do_system(gp_input_line + 1); if (interactive) /* 3.5 did it unconditionally */ (void) fputs("!\n", stderr); /* why do we need this ? */ return (0); } if_depth = 0; if_condition = TRUE; num_tokens = scanner(&gp_input_line, &gp_input_line_len); c_token = 0; while (c_token < num_tokens) { command(); if (command_exit_status) { command_exit_status = 0; return 1; } if (c_token < num_tokens) { /* something after command */ if (equals(c_token, ";")) c_token++; else int_error(c_token, "';' expected"); } } return (0); } void do_string(char *s, TBOOLEAN throwaway_s) { TBOOLEAN screen_was_ok = screen_ok; #ifdef USE_MOUSE if (display_ipc_commands()) fprintf(stderr, "%s\n", s); #endif lf_push(NULL); /* save state for errors and recursion */ lf_head->c_token = c_token; lf_head->num_tokens = num_tokens; lf_head->tokens = gp_alloc(num_tokens * sizeof(struct lexical_unit), "lf tokens"); memcpy(lf_head->tokens, token, num_tokens * sizeof(struct lexical_unit)); lf_head->input_line = gp_strdup(gp_input_line); while (gp_input_line_len < strlen(s) + 1) extend_input_line(); strcpy(gp_input_line, s); if (throwaway_s) free(s); screen_ok = FALSE; do_line(); screen_ok = screen_was_ok; lf_pop(); } #ifdef USE_MOUSE void toggle_display_of_ipc_commands() { if (mouse_setting.verbose) mouse_setting.verbose = 0; else mouse_setting.verbose = 1; } int display_ipc_commands() { return mouse_setting.verbose; } void do_string_replot(char *s) { char *orig_input_line = gp_strdup(gp_input_line); while (gp_input_line_len < strlen(s) + 1) extend_input_line(); strcpy(gp_input_line, s); if (display_ipc_commands()) fprintf(stderr, "%s\n", s); do_line(); #ifdef VOLATILE_REFRESH if (volatile_data && refresh_ok) { if (display_ipc_commands()) fprintf(stderr, "refresh\n"); refresh_request(); } else #endif if (!replot_disabled) replotrequest(); else int_warn(NO_CARET, "refresh not possible and replot is disabled"); strcpy(gp_input_line, orig_input_line); free(orig_input_line); } void restore_prompt() { if (interactive) { #if defined(HAVE_LIBREADLINE) rl_forced_update_display(); #else #if defined(HAVE_LIBEDITLINE) /* FIXME: editline does not support forced update, so this is probably not enough */ rl_redisplay(); #endif fputs(PROMPT, stderr); fflush(stderr); #endif } } #endif /* USE_MOUSE */ void define() { int start_token; /* the 1st token in the function definition */ struct udvt_entry *udv; struct udft_entry *udf; struct value result; if (equals(c_token + 1, "(")) { /* function ! */ int dummy_num = 0; struct at_type *at_tmp; char save_dummy[MAX_NUM_VAR][MAX_ID_LEN+1]; memcpy(save_dummy, c_dummy_var, sizeof(save_dummy)); start_token = c_token; do { c_token += 2; /* skip to the next dummy */ copy_str(c_dummy_var[dummy_num++], c_token, MAX_ID_LEN); } while (equals(c_token + 1, ",") && (dummy_num < MAX_NUM_VAR)); if (equals(c_token + 1, ",")) int_error(c_token + 2, "function contains too many parameters"); c_token += 3; /* skip (, dummy, ) and = */ if (END_OF_COMMAND) int_error(c_token, "function definition expected"); udf = dummy_func = add_udf(start_token); udf->dummy_num = dummy_num; if ((at_tmp = perm_at()) == (struct at_type *) NULL) int_error(start_token, "not enough memory for function"); if (udf->at) /* already a dynamic a.t. there */ free_at(udf->at); /* so free it first */ udf->at = at_tmp; /* before re-assigning it. */ memcpy(c_dummy_var, save_dummy, sizeof(save_dummy)); m_capture(&(udf->definition), start_token, c_token - 1); dummy_func = NULL; /* dont let anyone else use our workspace */ /* Save function definition in a user-accessible variable */ if (1) { char *tmpnam = gp_alloc(8+strlen(udf->udf_name), "varname"); strcpy(tmpnam, "GPFUN_"); strcat(tmpnam, udf->udf_name); fill_gpval_string(tmpnam, udf->definition); free(tmpnam); } } else { /* variable ! */ char *varname = gp_input_line + token[c_token].start_index; if (!strncmp(varname, "GPVAL_", 6) || !strncmp(varname, "MOUSE_", 6)) int_error(c_token, "Cannot set internal variables GPVAL_ and MOUSE_"); start_token = c_token; c_token += 2; udv = add_udv(start_token); (void) const_express(&result); /* Prevents memory leak if the variable name is re-used */ if (!udv->udv_undef) gpfree_string(&udv->udv_value); udv->udv_value = result; udv->udv_undef = FALSE; } } void undefine_command() { char key[MAX_ID_LEN+1]; struct udvt_entry **udv_ptr = &first_udv; c_token++; while (!END_OF_COMMAND) { copy_str(key, c_token, MAX_ID_LEN); if (strncmp(key, "GPVAL_", 6) && strncmp(key, "MOUSE_", 6)) { udv_ptr = &first_udv; while (*udv_ptr) { if (!strcmp(key, (*udv_ptr)->udv_name)) { (*udv_ptr)->udv_undef = TRUE; gpfree_string(&((*udv_ptr)->udv_value)); break; } udv_ptr = &((*udv_ptr)->next_udv); } } c_token++; } } static void command() { int i; for (i = 0; i < MAX_NUM_VAR; i++) c_dummy_var[i][0] = NUL; /* no dummy variables */ if (is_definition(c_token)) define(); else (*lookup_ftable(&command_ftbl[0],c_token))(); return; } /* process the 'raise' or 'lower' command */ void raise_lower_command(int lower) { ++c_token; if (END_OF_COMMAND) { if (lower) { #ifdef OS2 pm_lower_terminal_window(); #endif #ifdef X11 x11_lower_terminal_group(); #endif #ifdef _Windows win_lower_terminal_window(); #endif #ifdef WXWIDGETS wxt_lower_terminal_group(); #endif } else { #ifdef OS2 pm_raise_terminal_window(); #endif #ifdef X11 x11_raise_terminal_group(); #endif #ifdef _Windows win_raise_terminal_window(); #endif #ifdef WXWIDGETS wxt_raise_terminal_group(); #endif } return; } else { int number; int negative = equals(c_token, "-"); if (negative || equals(c_token, "+")) c_token++; if (!END_OF_COMMAND && isanumber(c_token)) { number = real_expression(); if (negative) number = -number; if (lower) { #ifdef OS2 pm_lower_terminal_window(); #endif #ifdef X11 x11_lower_terminal_window(number); #endif #ifdef _Windows win_lower_terminal_window(); #endif #ifdef WXWIDGETS wxt_lower_terminal_window(number); #endif } else { #ifdef OS2 pm_raise_terminal_window(); #endif #ifdef X11 x11_raise_terminal_window(number); #endif #ifdef _Windows win_raise_terminal_window(); #endif #ifdef WXWIDGETS wxt_raise_terminal_window(number); #endif } ++c_token; return; } } int_error(c_token, "usage: raise {x11_plot_n}"); } void raise_command(void) { raise_lower_command(0); } void lower_command(void) { raise_lower_command(1); } #ifdef USE_MOUSE #define WHITE_AFTER_TOKEN(x) \ (' ' == gp_input_line[token[x].start_index + token[x].length] \ || '\t' == gp_input_line[token[x].start_index + token[x].length] \ || '\0' == gp_input_line[token[x].start_index + token[x].length]) /* process the 'bind' command */ void bind_command() { char* lhs = (char*) 0; char* rhs = (char*) 0; TBOOLEAN allwindows = FALSE; ++c_token; if (!END_OF_COMMAND && equals(c_token,"!")) { bind_remove_all(); ++c_token; return; } if (!END_OF_COMMAND && almost_equals(c_token,"all$windows")) { allwindows = TRUE; c_token++; } /* get left hand side: the key or key sequence */ if (!END_OF_COMMAND) { char* first = gp_input_line + token[c_token].start_index; int size = (int) (strchr(first, ' ') - first); if (size < 0) { size = (int) (strchr(first, '\0') - first); } if (size < 0) { fprintf(stderr, "(bind_command) %s:%d\n", __FILE__, __LINE__); return; } lhs = (char*) gp_alloc(size + 1, "bind_command->lhs"); if (isstring(c_token)) { quote_str(lhs, c_token, token_len(c_token)); } else { char* ptr = lhs; while (!END_OF_COMMAND) { copy_str(ptr, c_token, token_len(c_token) + 1); ptr += token_len(c_token); if (WHITE_AFTER_TOKEN(c_token)) { break; } ++c_token; } } ++c_token; } /* get right hand side: the command. allocating the size * of gp_input_line is too big, but shouldn't hurt too much. */ if (!END_OF_COMMAND) { rhs = (char*) gp_alloc(strlen(gp_input_line) + 1, "bind_command->rhs"); if (isstring(c_token)) { /* bind "..." */ quote_str(rhs, c_token, token_len(c_token)); c_token++; } else { char* ptr = rhs; while (!END_OF_COMMAND) { /* bind ... ... ... */ copy_str(ptr, c_token, token_len(c_token) + 1); ptr += token_len(c_token); if (WHITE_AFTER_TOKEN(c_token)) { *ptr++ = ' '; *ptr = '\0'; } c_token++; } } } FPRINTF((stderr, "(bind_command) |%s| |%s|\n", lhs, rhs)); /* bind_process() will eventually free lhs / rhs ! */ bind_process(lhs, rhs, allwindows); } #endif /* USE_MOUSE */ /* * Command parser functions */ /* process the 'call' command */ void call_command() { char *save_file = NULL; c_token++; save_file = try_to_get_string(); if (!save_file) int_error(c_token, "expecting filename"); gp_expand_tilde(&save_file); /* Argument list follows filename */ load_file(loadpath_fopen(save_file, "r"), save_file, TRUE); } /* process the 'cd' command */ void changedir_command() { char *save_file = NULL; c_token++; save_file = try_to_get_string(); if (!save_file) int_error(c_token, "expecting directory name"); gp_expand_tilde(&save_file); if (changedir(save_file)) int_error(c_token, "Can't change to this directory"); else update_gpval_variables(5); free(save_file); } /* process the 'clear' command */ void clear_command() { term_start_plot(); if (multiplot && term->fillbox) { unsigned int xx1 = (unsigned int) (xoffset * term->xmax); unsigned int yy1 = (unsigned int) (yoffset * term->ymax); unsigned int width = (unsigned int) (xsize * term->xmax); unsigned int height = (unsigned int) (ysize * term->ymax); (*term->fillbox) (0, xx1, yy1, width, height); } term_end_plot(); screen_ok = FALSE; c_token++; } /* process the 'evaluate' command */ void eval_command() { int save_token = ++c_token; char *command; if (++eval_depth > 4) int_error(save_token, "Deep recursion in evaluate"); if (!(command = try_to_get_string())) int_error(c_token, "Expected command string"); do_string(command, TRUE); --eval_depth; } /* reset eval_depth counter */ void reset_eval_depth() { eval_depth = 0; } /* process the 'exit' and 'quit' commands */ void exit_command() { /* If the command is "exit gnuplot" then do so */ if (equals(c_token+1,"gnuplot")) exit(0); /* else graphics will be tidied up in main */ command_exit_status = 1; } /* fit_command() is in fit.c */ /* help_command() is below */ /* process the 'history' command */ void history_command() { #if defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE) c_token++; if (!END_OF_COMMAND && equals(c_token,"?")) { static char *search_str = NULL; /* string from command line to search for */ /* find and show the entries */ c_token++; m_capture(&search_str, c_token, c_token); /* reallocates memory */ printf ("history ?%s\n", search_str); if (!history_find_all(search_str)) int_error(c_token,"not in history"); c_token++; } else if (!END_OF_COMMAND && equals(c_token,"!")) { char *search_str = NULL; /* string from command line to search for */ const char *line_to_do = NULL; /* command to execute at end if necessary */ int c_token_copy; static char *gpil_copy = NULL; c_token++; m_capture(&search_str, c_token, c_token); line_to_do = history_find(search_str); free(search_str); if (line_to_do == NULL) int_error(c_token, "not in history"); /* Must keep current input line in case there are some remaining lines * to process after a semicolon. However, could int_error() some where * during do_line() so a static copy is kept. */ free(gpil_copy); gpil_copy = gp_strdup(gp_input_line); c_token_copy = c_token; while (gp_input_line_len < strlen(line_to_do) + 1) extend_input_line(); strcpy(gp_input_line, line_to_do); if (scanner(&gp_input_line, &gp_input_line_len)) { if (almost_equals(0, "hi$story") && equals(1, "!")) int_error(c_token-1,"petitio principii"); /* Oops... infinite loop */ else { printf(" Executing:\n\t%s\n", line_to_do); do_line(); } } /* Restore previous state of line and parser, gpil_copy will be freed next time */ strcpy(gp_input_line, gpil_copy); num_tokens = scanner(&gp_input_line, &gp_input_line_len); c_token = c_token_copy + 1; } else { int n = 0; /* print only entries */ TBOOLEAN append = FALSE; /* rewrite output file or append it */ static char *name = NULL; /* name of the output file; NULL for stdout */ TBOOLEAN quiet = FALSE; if (!END_OF_COMMAND && almost_equals(c_token,"q$uiet")) { /* option quiet to suppress history entry numbers */ quiet = TRUE; c_token++; } /* show history entries */ if (!END_OF_COMMAND && isanumber(c_token)) { n = int_expression(); } free(name); if ((name = try_to_get_string())) { if (!END_OF_COMMAND && almost_equals(c_token, "ap$pend")) { append = TRUE; c_token++; } } write_history_n(n, (quiet ? "" : name), (append ? "a" : "w")); } #else c_token++; int_warn(NO_CARET, "You have to compile gnuplot with builtin readline or GNU readline to enable history support."); #endif /* defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE) */ } #define REPLACE_ELSE(tok) \ do { \ int idx = token[tok].start_index; \ token[tok].length = 1; \ gp_input_line[idx++] = ';'; /* e */ \ gp_input_line[idx++] = ' '; /* l */ \ gp_input_line[idx++] = ' '; /* s */ \ gp_input_line[idx++] = ' '; /* e */ \ } while (0) #if 0 #define PRINT_TOKEN(tok) \ do { \ int i; \ int end_index = token[tok].start_index + token[tok].length; \ for (i = token[tok].start_index; i < end_index && gp_input_line[i]; i++) { \ fputc(gp_input_line[i], stderr); \ } \ fputc('\n', stderr); \ fflush(stderr); \ } while (0) #endif /* process the 'if' command */ void if_command() { double exprval; if_depth++; if (!equals(++c_token, "(")) /* no expression */ int_error(c_token, "expecting (expression)"); exprval = real_expression(); if (exprval != 0.0) { /* fake the condition of a ';' between commands */ int eolpos = token[num_tokens - 1].start_index + token[num_tokens - 1].length; --c_token; token[c_token].length = 1; token[c_token].start_index = eolpos + 2; gp_input_line[eolpos + 2] = ';'; gp_input_line[eolpos + 3] = NUL; if_condition = TRUE; } else { while (c_token < num_tokens) { /* skip over until the next command */ while (!END_OF_COMMAND) { ++c_token; } if (++c_token < num_tokens && (equals(c_token, "else"))) { /* break if an "else" was found */ if_condition = FALSE; --c_token; /* go back to ';' */ return; } } /* no else found */ c_token = num_tokens = 0; } } /* process the 'else' command */ void else_command() { if (if_depth <= 0) { int_error(c_token, "else without if"); return; } else { if_depth--; } if (TRUE == if_condition) { /* First part of line was true so * discard the rest of the line. */ c_token = num_tokens = 0; } else { REPLACE_ELSE(c_token); if_condition = TRUE; } } /* process the 'load' command */ void load_command() { FILE *fp; char *save_file; c_token++; save_file = try_to_get_string(); if (!save_file) int_error(c_token, "expecting filename"); gp_expand_tilde(&save_file); fp = strcmp(save_file, "-") ? loadpath_fopen(save_file, "r") : stdout; load_file(fp, save_file, FALSE); } /* null command */ void null_command() { return; } /* process the 'pause' command */ void pause_command() { int text = 0; double sleep_time; static char *buf = NULL; c_token++; #ifdef USE_MOUSE paused_for_mouse = 0; if (equals(c_token,"mouse")) { sleep_time = -1; c_token++; /* EAM FIXME - This is not the correct test; what we really want */ /* to know is whether or not the terminal supports mouse feedback */ /* if (term_initialised) { */ if (mouse_setting.on && term) { struct udvt_entry *current; int end_condition = 0; while (!(END_OF_COMMAND)) { if (almost_equals(c_token,"key$press")) { end_condition |= PAUSE_KEYSTROKE; c_token++; } else if (equals(c_token,",")) { c_token++; } else if (equals(c_token,"any")) { end_condition |= PAUSE_ANY; c_token++; } else if (equals(c_token,"button1")) { end_condition |= PAUSE_BUTTON1; c_token++; } else if (equals(c_token,"button2")) { end_condition |= PAUSE_BUTTON2; c_token++; } else if (equals(c_token,"button3")) { end_condition |= PAUSE_BUTTON3; c_token++; } else if (equals(c_token,"close")) { end_condition |= PAUSE_WINCLOSE; c_token++; } else break; } if (end_condition) paused_for_mouse = end_condition; else paused_for_mouse = PAUSE_CLICK; /* Set the pause mouse return codes to -1 */ current = add_udv_by_name("MOUSE_KEY"); current->udv_undef = FALSE; Ginteger(¤t->udv_value,-1); current = add_udv_by_name("MOUSE_BUTTON"); current->udv_undef = FALSE; Ginteger(¤t->udv_value,-1); } else int_warn(NO_CARET,"Mousing not active"); } else #endif sleep_time = real_expression(); if (END_OF_COMMAND) { if (!buf) /* Can only happen the first time through */ buf = gp_strdup("paused"); } else { free(buf); buf = try_to_get_string(); if (!buf) int_error(c_token, "expecting string"); else { #ifdef _Windows if (sleep_time >= 0) #elif defined(OS2) if (strcmp(term->name, "pm") != 0 || sleep_time >= 0) #endif /* _Windows */ fputs(buf, stderr); text = 1; } } if (sleep_time < 0) { #if defined(_Windows) && !defined(WGP_CONSOLE) if (paused_for_mouse && !graphwin.hWndGraph) { if (interactive) { /* cannot wait for Enter in a non-interactive session without the graph window */ char tmp[512]; if (buf) fprintf(stderr,"%s\n", buf); fgets(tmp, 512, stdin); /* graphical window not yet initialized, wait for any key here */ } } else { /* pausing via graphical windows */ int tmp = paused_for_mouse; if (buf && paused_for_mouse) fprintf(stderr,"%s\n", buf); if (!Pause(buf)) { if (!tmp) { bail_to_command_line(); } else { if (!graphwin.hWndGraph) bail_to_command_line(); } } } #elif defined(OS2) if (strcmp(term->name, "pm") == 0 && sleep_time < 0) { int rc; if ((rc = PM_pause(buf)) == 0) { /* if (!CallFromRexx) * would help to stop REXX programs w/o raising an error message * in RexxInterface() ... */ bail_to_command_line(); } else if (rc == 2) { fputs(buf, stderr); text = 1; (void) fgets(buf, strlen(buf), stdin); } } #elif defined(_Macintosh) if (strcmp(term->name, "macintosh") == 0 && sleep_time < 0) Pause( (int)sleep_time ); #else /* !(_Windows || OS2 || _Macintosh) */ #ifdef USE_MOUSE if (term && term->waitforinput) { /* term->waitforinput() will return, * if CR was hit */ term->waitforinput(); } else { #endif /* USE_MOUSE */ (void) fgets(buf, sizeof(buf), stdin); /* Hold until CR hit. */ #ifdef USE_MOUSE } #endif /* USE_MOUSE */ #endif /* !(_Windows || OS2 || _Macintosh) */ } if (sleep_time > 0) GP_SLEEP(sleep_time); if (text != 0 && sleep_time >= 0) fputc('\n', stderr); screen_ok = FALSE; } /* process the 'plot' command */ void plot_command() { plot_token = c_token++; plotted_data_from_stdin = FALSE; SET_CURSOR_WAIT; #ifdef USE_MOUSE plot_mode(MODE_PLOT); add_udv_by_name("MOUSE_X")->udv_undef = TRUE; add_udv_by_name("MOUSE_Y")->udv_undef = TRUE; add_udv_by_name("MOUSE_X2")->udv_undef = TRUE; add_udv_by_name("MOUSE_Y2")->udv_undef = TRUE; add_udv_by_name("MOUSE_BUTTON")->udv_undef = TRUE; add_udv_by_name("MOUSE_SHIFT")->udv_undef = TRUE; add_udv_by_name("MOUSE_ALT")->udv_undef = TRUE; add_udv_by_name("MOUSE_CTRL")->udv_undef = TRUE; #endif plotrequest(); SET_CURSOR_ARROW; } void print_set_output(char *name, TBOOLEAN append_p) { if (print_out && print_out != stderr && print_out != stdout) { #ifdef PIPES if (print_out_name[0] == '|') { if (0 > pclose(print_out)) perror(print_out_name); } else #endif if (0 > fclose(print_out)) perror(print_out_name); } if (print_out_name) free(print_out_name); print_out_name = NULL; if (! name) { print_out = stderr; return; } if (! strcmp(name, "-")) { print_out = stdout; return; } #ifdef PIPES if (name[0]=='|') { print_out = popen(name + 1, "w"); if (!print_out) perror(name); else print_out_name = name; return; } #endif print_out = fopen(name, append_p ? "a" : "w"); if (!print_out) { perror(name); return; } print_out_name = name; } char * print_show_output() { if (print_out==stdout) return ""; if (!print_out || print_out==stderr || !print_out_name) return ""; return print_out_name; } /* process the 'print' command */ void print_command() { struct value a; /* space printed between two expressions only */ int need_space = 0; if (!print_out) { print_out = stderr; } screen_ok = FALSE; do { ++c_token; const_express(&a); if (a.type == STRING) { fputs(a.v.string_val, print_out); gpfree_string(&a); need_space = 0; } else { if (need_space) putc(' ', print_out); disp_value(print_out, &a, FALSE); need_space = 1; } } while (!END_OF_COMMAND && equals(c_token, ",")); (void) putc('\n', print_out); fflush(print_out); } /* process the 'pwd' command */ void pwd_command() { char *save_file = NULL; save_file = (char *) gp_alloc(PATH_MAX, "print current dir"); if (save_file) { GP_GETCWD(save_file, PATH_MAX); fprintf(stderr, "%s\n", save_file); free(save_file); } c_token++; } #ifdef VOLATILE_REFRESH /* EAM April 2007 * The "refresh" command replots the previous graph without going back to read * the original data. This allows zooming or other operations on data that was * only transiently available in the input stream. */ void refresh_command() { c_token++; refresh_request(); } void refresh_request() { FPRINTF((stderr,"refresh_request\n")); if ((first_plot == NULL && refresh_ok == 2) || (first_3dplot == NULL && refresh_ok == 3)) int_error(NO_CARET, "no active plot; cannot refresh"); if (refresh_ok == 0) { int_warn(NO_CARET, "cannot refresh from this state. trying full replot"); replotrequest(); return; } /* If the state has been reset to autoscale since the last plot, * initialize the axis limits. */ AXIS_INIT2D_REFRESH(FIRST_X_AXIS,TRUE); AXIS_INIT2D_REFRESH(FIRST_Y_AXIS,TRUE); AXIS_INIT2D_REFRESH(SECOND_X_AXIS,TRUE); AXIS_INIT2D_REFRESH(SECOND_Y_AXIS,TRUE); AXIS_UPDATE2D_REFRESH(T_AXIS); /* Untested: T and R want INIT2D or UPDATE2D?? */ AXIS_UPDATE2D_REFRESH(R_AXIS); /* It doesn't matter, they are used for functions, not for data */ AXIS_UPDATE2D_REFRESH(FIRST_Z_AXIS); AXIS_UPDATE2D_REFRESH(COLOR_AXIS); if (refresh_ok == 2) refresh_bounds(first_plot, refresh_nplots); else if (refresh_ok == 3) refresh_3dbounds(first_3dplot, refresh_nplots); /* FIXME EAM * The existing mechanism defined in axis.h does not work here for some reason. * The following defined check works empirically. Most of the time. Maybe. */ #undef CHECK_REVERSE #define CHECK_REVERSE(axis) do { \ AXIS *ax = axis_array + axis; \ if ((ax->range_flags & RANGE_REVERSE)) { \ double temp = ax->max; \ ax->max = ax->min; ax->min = temp; \ } } while (0) CHECK_REVERSE(FIRST_X_AXIS); CHECK_REVERSE(FIRST_Y_AXIS); CHECK_REVERSE(SECOND_X_AXIS); CHECK_REVERSE(SECOND_Y_AXIS); #undef CHECK_REVERSE if (refresh_ok == 2) do_plot(first_plot, refresh_nplots); else if (refresh_ok == 3) do_3dplot(first_3dplot, refresh_nplots, 0); else int_error(NO_CARET, "Internal error - refresh of unknown plot type"); } #endif /* process the 'replot' command */ void replot_command() { if (!*replot_line) int_error(c_token, "no previous plot"); /* Disable replot for some reason; currently used by the mouse/hotkey capable terminals to avoid replotting when some data come from stdin, i.e. when plotted_data_from_stdin==1 after plot "-". */ if (replot_disabled) { replot_disabled = FALSE; bail_to_command_line(); /* be silent --- don't mess the screen */ } if (!term) /* unknown terminal */ int_error(c_token, "use 'set term' to set terminal type first"); c_token++; SET_CURSOR_WAIT; if (term->flags & TERM_INIT_ON_REPLOT) term->init(); replotrequest(); SET_CURSOR_ARROW; } /* process the 'reread' command */ void reread_command() { FILE *fp = lf_top(); if (fp != (FILE *) NULL) rewind(fp); c_token++; } /* process the 'save' command */ void save_command() { FILE *fp; char *save_file = NULL; int what; c_token++; what = lookup_table(&save_tbl[0], c_token); switch (what) { case SAVE_FUNCS: case SAVE_SET: case SAVE_TERMINAL: case SAVE_VARS: c_token++; break; default: break; } save_file = try_to_get_string(); if (!save_file) int_error(c_token, "expecting filename"); #ifdef PIPES if (save_file[0]=='|') fp = popen(save_file+1,"w"); else #endif { gp_expand_tilde(&save_file); fp = strcmp(save_file,"-") ? loadpath_fopen(save_file,"w") : stdout; } if (!fp) os_error(c_token, "Cannot open save file"); switch (what) { case SAVE_FUNCS: save_functions(fp); break; case SAVE_SET: save_set(fp); break; case SAVE_TERMINAL: save_term(fp); break; case SAVE_VARS: save_variables(fp); break; default: save_all(fp); } if (stdout != fp) { #ifdef PIPES if (save_file[0] == '|') (void) pclose(fp); else #endif (void) fclose(fp); } free(save_file); } /* process the 'screendump' command */ void screendump_command() { c_token++; #ifdef _Windows screen_dump(); #else fputs("screendump not implemented\n", stderr); #endif } /* set_command() is in set.c */ /* 'shell' command is processed by do_shell(), see below */ /* show_command() is in show.c */ /* process the 'splot' command */ void splot_command() { plot_token = c_token++; plotted_data_from_stdin = FALSE; SET_CURSOR_WAIT; #ifdef USE_MOUSE plot_mode(MODE_SPLOT); add_udv_by_name("MOUSE_X")->udv_undef = TRUE; add_udv_by_name("MOUSE_Y")->udv_undef = TRUE; add_udv_by_name("MOUSE_X2")->udv_undef = TRUE; add_udv_by_name("MOUSE_Y2")->udv_undef = TRUE; add_udv_by_name("MOUSE_BUTTON")->udv_undef = TRUE; #endif plot3drequest(); SET_CURSOR_ARROW; } /* process the 'system' command */ void system_command() { char *cmd; ++c_token; cmd = try_to_get_string(); do_system(cmd); free(cmd); } /* process the 'test palette' command * * note 1: it works on terminals supporting as well as not supporting pm3d * note 2: due to the call to load_file(), the rest of the current command * line after 'test palette ;' is discarded */ static void test_palette_subcommand() { enum {test_palette_colors = 256}; double gray, z[test_palette_colors]; rgb_color rgb1[test_palette_colors]; int i; static const char pre1[] = "\ reset;set multi;\ uns border;uns key;set tic in;uns xtics;uns ytics;\ se cbtic 0,0.1,1 mirr format '';\ se xr[0:1];se yr[0:1];se zr[0:1];se cbr[0:1];\ se pm3d map;set colorbox hor user orig 0.05,0.02 size 0.925,0.12;"; static const char pre2[] = "splot 1/0;\n\n\n"; /* note: those \n's are because of x11 terminal problems with blocking pipe */ static const char pre3[] = "\ uns pm3d;\ se lmarg scre 0.05;se rmarg scre 0.975; se bmarg scre 0.22; se tmarg scre 0.86;\ se grid;se tics scale 0; se xtics 0,0.1;se ytics 0,0.1;\ se key top right at scre 0.975,0.975 horizontal \ title 'R,G,B profiles of the current color palette';"; static const char post[] = "\ \n\n\nuns multi;\n"; /* no final 'reset' in favour of mousing */ int can_pm3d = (term->make_palette && term->set_color); char *order = "rgb"; char *save_replot_line; TBOOLEAN save_is_3d_plot; FILE *f = tmpfile(); c_token++; /* parse optional option */ if (!END_OF_COMMAND) { int err = (token[c_token].length != 3); order = gp_input_line + token[c_token].start_index; if (!err) { err += (memchr(order, 'r', 3) == NULL); err += (memchr(order, 'g', 3) == NULL); err += (memchr(order, 'b', 3) == NULL); } if (err) int_error(c_token, "combination rgb or gbr or brg etc. expected"); c_token++; } if (!f) int_error(NO_CARET, "cannot write temporary file"); /* generate r,g,b curves */ for (i = 0; i < test_palette_colors; i++) { /* colours equidistantly from [0,1] */ z[i] = (double)i / (test_palette_colors - 1); gray = (sm_palette.positive == SMPAL_NEGATIVE) ? 1-z[i] : z[i]; rgb1_from_gray(gray, &rgb1[i]); } /* commands to setup the test palette plot */ enable_reset_palette = 0; save_replot_line = gp_strdup(replot_line); save_is_3d_plot = is_3d_plot; fputs(pre1, f); if (can_pm3d) fputs(pre2, f); fputs(pre3, f); /* put inline data of the r,g,b curves */ fputs("p", f); for (i=0; i 0) fputs(",", f); fputs("'-'tit'", f); switch (order[i]) { case 'r': fputs("red'w l lt 1", f); break; case 'g': fputs("green'w l lt 2", f); break; case 'b': fputs("blue'w l lt 3", f); break; } /* switch(order[i]) */ } /* for (i) */ fputs("\n", f); for (i = 0; i < 3; i++) { int k, c = order[i]; for (k = 0; k < test_palette_colors; k++) { double rgb = (c=='r') ? rgb1[k].r : ((c=='g') ? rgb1[k].g : rgb1[k].b); fprintf(f, "%0.4f\t%0.4f\n", z[k], rgb); } fputs("e\n", f); } fputs(post, f); /* save current gnuplot 'set' status because of the tricky sets * for our temporary testing plot. */ save_set(f); /* execute all commands from the temporary file */ rewind(f); load_file(f, NULL, FALSE); /* note: it does fclose(f) */ /* enable reset_palette() and restore replot line */ enable_reset_palette = 1; free(replot_line); replot_line = save_replot_line; is_3d_plot = save_is_3d_plot; /* further, gp_input_line[] and token[] now destroyed! */ c_token = num_tokens = 0; } /* process the undocumented 'test time' command * test time 'format' 'string' * to assist testing of time routines */ static void test_time_subcommand() { char *format = NULL; char *string = NULL; struct tm tm; double secs; /* given a format and a time string, exercise the time code */ if (isstring(++c_token)) { m_quote_capture(&format, c_token, c_token); if (isstring(++c_token)) { m_quote_capture(&string, c_token, c_token); memset(&tm, 0, sizeof(tm)); gstrptime(string, format, &tm); secs = gtimegm(&tm); fprintf(stderr, "internal = %f - %d/%d/%d::%d:%d:%d , wday=%d, yday=%d\n", secs, tm.tm_mday, tm.tm_mon + 1, tm.tm_year % 100, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday, tm.tm_yday); memset(&tm, 0, sizeof(tm)); ggmtime(&tm, secs); gstrftime(string, strlen(string), format, secs); fprintf(stderr, "convert back \"%s\" - %d/%d/%d::%d:%d:%d , wday=%d, yday=%d\n", string, tm.tm_mday, tm.tm_mon + 1, tm.tm_year % 100, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday, tm.tm_yday); free(string); ++c_token; } /* else: expecting time string */ free(format); } /* else: expecting format string */ } /* process the 'test' command */ void test_command() { int what; c_token++; if (END_OF_COMMAND) { test_term(); return; } what = lookup_table(&test_tbl[0], c_token); switch (what) { case TEST_TERMINAL: test_term(); break; case TEST_PALETTE: test_palette_subcommand(); break; case TEST_TIME: test_time_subcommand(); break; default: int_error(c_token, "only keywords are 'terminal' and 'palette'"); } } /* unset_command is in unset.c */ /* process the 'update' command */ void update_command() { /* old parameter filename */ char *opfname = NULL; /* new parameter filename */ char *npfname = NULL; c_token++; if (!(opfname = try_to_get_string())) int_error(c_token, "Parameter filename expected"); if (!END_OF_COMMAND && !(npfname = try_to_get_string())) int_error(c_token, "New parameter filename expected"); update(opfname, npfname); free(npfname); free(opfname); } /* process invalid commands and, on OS/2, REXX commands */ void invalid_command() { #ifdef OS2 if (token[c_token].is_token) { int rc; rc = ExecuteMacro(gp_input_line + token[c_token].start_index, token[c_token].length); if (rc == 0) { c_token = num_tokens = 0; return; } } #endif int_error(c_token, "invalid command"); } /* * Auxiliary routines */ /* used by changedir_command() */ static int changedir(char *path) { #if defined(MSDOS) || defined(WIN16) || defined(DOS386) # if defined(__ZTC__) unsigned dummy; /* it's a parameter needed for dos_setdrive */ # endif /* first deal with drive letter */ if (isalpha(path[0]) && (path[1] == ':')) { int driveno = toupper(path[0]) - 'A'; /* 0=A, 1=B, ... */ # if defined(__ZTC__) (void) dos_setdrive(driveno + 1, &dummy); # endif # if (defined(MSDOS) && defined(__EMX__)) || defined(__MSC__) (void) _chdrive(driveno + 1); # endif /* HBB: recent versions of DJGPP also have setdisk():, * so I del'ed the special code */ # if ((defined(MSDOS) || defined(_Windows)) && defined(__TURBOC__)) || defined(DJGPP) (void) setdisk(driveno); # endif path += 2; /* move past drive letter */ } /* then change to actual directory */ if (*path) if (chdir(path)) return 1; return 0; /* should report error with setdrive also */ #elif defined(WIN32) return !(SetCurrentDirectory(path)); #elif defined(__EMX__) && defined(OS2) return _chdir2(path); #else return chdir(path); #endif /* MSDOS etc. */ } /* used by replot_command() */ void replotrequest() { if (equals(c_token, "[")) int_error(c_token, "cannot set range with replot"); /* do not store directly into the replot_line string, until the * new plot line has been successfully plotted. This way, * if user makes a typo in a replot line, they do not have * to start from scratch. The replot_line will be committed * after do_plot has returned, whence we know all is well */ if (END_OF_COMMAND) { char *rest_args = &gp_input_line[token[c_token].start_index]; size_t replot_len = strlen(replot_line); size_t rest_len = strlen(rest_args); /* preserve commands following 'replot ;' */ /* move rest of input line to the start * necessary because of realloc() in extend_input_line() */ memmove(gp_input_line,rest_args,rest_len+1); /* reallocs if necessary */ while (gp_input_line_len < replot_len+rest_len+1) extend_input_line(); /* move old rest args off begin of input line to * make space for replot_line */ memmove(gp_input_line+replot_len,gp_input_line,rest_len+1); /* copy previous plot command to start of input line */ memcpy(gp_input_line, replot_line, replot_len); } else { char *replot_args = NULL; /* else m_capture will free it */ int last_token = num_tokens - 1; /* length = length of old part + length of new part + ", " + \0 */ size_t newlen = strlen(replot_line) + token[last_token].start_index + token[last_token].length - token[c_token].start_index + 3; m_capture(&replot_args, c_token, last_token); /* might be empty */ while (gp_input_line_len < newlen) extend_input_line(); strcpy(gp_input_line, replot_line); strcat(gp_input_line, ", "); strcat(gp_input_line, replot_args); free(replot_args); } plot_token = 0; /* whole line to be saved as replot line */ screen_ok = FALSE; num_tokens = scanner(&gp_input_line, &gp_input_line_len); c_token = 1; /* skip the 'plot' part */ if (is_3d_plot) plot3drequest(); else plotrequest(); } /* Is 'set view map' currently working inside 'splot' or not? Calculation of * mouse coordinates and the corresponding routines must know it, because * 'splot' can be either true 3D plot or a 2D map. * This flag is set when entering splot command and 'set view map', i.e. by * splot_map_activate(), and reset when calling splot_map_deactivate(). */ static int splot_map_active = 0; /* Store values reset by 'set view map' during splot, used by those two * routines below. */ static float splot_map_surface_rot_x; static float splot_map_surface_rot_z; static float splot_map_surface_scale; /* This routine is called at the beginning of 'splot'. It sets up some splot * parameters needed to present the 'set view map'. */ void splot_map_activate() { if (splot_map_active) return; splot_map_active = 1; /* save current values */ splot_map_surface_rot_x = surface_rot_x; splot_map_surface_rot_z = surface_rot_z ; splot_map_surface_scale = surface_scale; /* set new values */ surface_rot_x = 180; surface_rot_z = 0; surface_scale = 1.3; axis_array[FIRST_Y_AXIS].range_flags ^= RANGE_REVERSE; axis_array[SECOND_Y_AXIS].range_flags ^= RANGE_REVERSE; /* note: ^ is xor */ } /* This routine is called when the current 'set view map' is no more needed, * i.e., when calling "plot" --- the reversed y-axis et al must still be * available for mousing. */ void splot_map_deactivate() { if (!splot_map_active) return; splot_map_active = 0; /* restore the original values */ surface_rot_x = splot_map_surface_rot_x; surface_rot_z = splot_map_surface_rot_z; surface_scale = splot_map_surface_scale; axis_array[FIRST_Y_AXIS].range_flags ^= RANGE_REVERSE; axis_array[SECOND_Y_AXIS].range_flags ^= RANGE_REVERSE; /* note: ^ is xor */ } /* Support for input, shell, and help for various systems */ #ifdef VMS # include # include # include # include # include extern lib$get_input(), lib$put_output(); extern smg$read_composed_line(); extern sys$putmsg(); extern lbr$output_help(); extern lib$spawn(); int vms_len; unsigned int status[2] = { 1, 0 }; static char Help[MAX_LINE_LEN+1] = "gnuplot"; $DESCRIPTOR(prompt_desc, PROMPT); /* temporary fix until change to variable length */ struct dsc$descriptor_s line_desc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}; $DESCRIPTOR(help_desc, Help); $DESCRIPTOR(helpfile_desc, "GNUPLOT$HELP"); /* HBB 990829: confirmed this to be used on VMS, only --> moved into * the VMS-specific section */ void done(int status) { term_reset(); exit(status); } /* please note that the vms version of read_line doesn't support variable line length (yet) */ static int read_line(const char *prompt) { int more, start = 0; char expand_prompt[40]; current_prompt = prompt; /* HBB NEW 20040727 */ prompt_desc.dsc$w_length = strlen(prompt); prompt_desc.dsc$a_pointer = (char *) prompt; strcpy(expand_prompt, "_"); strncat(expand_prompt, prompt, 38); do { line_desc.dsc$w_length = MAX_LINE_LEN - start; line_desc.dsc$a_pointer = &gp_input_line[start]; switch (status[1] = smg$read_composed_line(&vms_vkid, &vms_ktid, &line_desc, &prompt_desc, &vms_len)) { case SMG$_EOF: done(EXIT_SUCCESS); /* ^Z isn't really an error */ break; case RMS$_TNS: /* didn't press return in time */ vms_len--; /* skip the last character */ break; /* and parse anyway */ case RMS$_BES: /* Bad Escape Sequence */ case RMS$_PES: /* Partial Escape Sequence */ sys$putmsg(status); vms_len = 0; /* ignore the line */ break; case SS$_NORMAL: break; /* everything's fine */ default: done(status[1]); /* give the error message */ } start += vms_len; gp_input_line[start] = NUL; inline_num++; if (gp_input_line[start - 1] == '\\') { /* Allow for a continuation line. */ prompt_desc.dsc$w_length = strlen(expand_prompt); prompt_desc.dsc$a_pointer = expand_prompt; more = 1; --start; } else { line_desc.dsc$w_length = strlen(gp_input_line); line_desc.dsc$a_pointer = gp_input_line; more = 0; } } while (more); return 0; } # ifdef NO_GIH void help_command() { int first = c_token; while (!END_OF_COMMAND) ++c_token; strcpy(Help, "GNUPLOT "); capture(Help + 8, first, c_token - 1, sizeof(Help) - 9); help_desc.dsc$w_length = strlen(Help); if ((vaxc$errno = lbr$output_help(lib$put_output, 0, &help_desc, &helpfile_desc, 0, lib$get_input)) != SS$_NORMAL) os_error(NO_CARET, "can't open GNUPLOT$HELP"); } # endif /* NO_GIH */ void do_shell() { screen_ok = FALSE; c_token++; if ((vaxc$errno = lib$spawn()) != SS$_NORMAL) { os_error(NO_CARET, "spawn error"); } } static void do_system(const char *cmd) { if (!cmd) return; /* gp_input_line is filled by read_line or load_file, but * line_desc length is set only by read_line; adjust now */ line_desc.dsc$w_length = strlen(cmd); line_desc.dsc$a_pointer = (char *) cmd; if ((vaxc$errno = lib$spawn(&line_desc)) != SS$_NORMAL) os_error(NO_CARET, "spawn error"); (void) putc('\n', stderr); } #endif /* VMS */ #ifdef NO_GIH #if defined(_Windows) void help_command() { if (END_OF_COMMAND) WinHelp(textwin.hWndParent, (LPSTR) winhelpname, HELP_INDEX, (DWORD) NULL); else { char buf[128]; int start = ++c_token; while (!(END_OF_COMMAND)) c_token++; capture(buf, start, c_token - 1, 128); WinHelp(textwin.hWndParent, (LPSTR) winhelpname, HELP_PARTIALKEY, (DWORD) buf); } } #else /* !_Windows */ void help_command() { while (!(END_OF_COMMAND)) c_token++; fputs("This gnuplot was not built with inline help\n", stderr); } #endif /* _Windows */ #endif /* NO_GIH */ /* * help_command: (not VMS, although it would work) Give help to the user. It * parses the command line into helpbuf and supplies help for that string. * Then, if there are subtopics available for that key, it prompts the user * with this string. If more input is given, help_command is called * recursively, with argument 0. Thus a more specific help can be supplied. * This can be done repeatedly. If null input is given, the function returns, * effecting a backward climb up the tree. * David Kotz (David.Kotz@Dartmouth.edu) 10/89 * drd - The help buffer is first cleared when called with toplevel=1. * This is to fix a bug where help is broken if ^C is pressed whilst in the * help. * Lars - The "int toplevel" argument is gone. I have converted it to a * static variable. * * FIXME - helpbuf is never free()'d */ #ifndef NO_GIH void help_command() { static char *helpbuf = NULL; static char *prompt = NULL; static int toplevel = 1; int base; /* index of first char AFTER help string */ int len; /* length of current help string */ TBOOLEAN more_help; TBOOLEAN only; /* TRUE if only printing subtopics */ TBOOLEAN subtopics; /* 0 if no subtopics for this topic */ int start; /* starting token of help string */ char *help_ptr; /* name of help file */ # if defined(SHELFIND) static char help_fname[256] = ""; /* keep helpfilename across calls */ # endif if ((help_ptr = getenv("GNUHELP")) == (char *) NULL) # ifndef SHELFIND /* if can't find environment variable then just use HELPFILE */ /* patch by David J. Liu for getting GNUHELP from home directory */ # if (defined(__TURBOC__) && (defined(MSDOS) || defined(DOS386))) || defined(__DJGPP__) help_ptr = HelpFile; # else /* __TURBOC__ */ help_ptr = HELPFILE; # endif #ifdef OS2 { /* look in the path where the executable lives */ static char buf[MAXPATHLEN]; char *ptr; _execname(buf, sizeof(buf)); _fnslashify(buf); ptr=strrchr(buf, '/'); if (ptr) { *(ptr+1)='\0'; strcat(buf, HELPFILE); help_ptr=&buf[0]; } else help_ptr = HELPFILE; } #endif /* end of patch - DJL */ # else /* !SHELFIND */ /* try whether we can find the helpfile via shell_find. If not, just use the default. (tnx Andreas) */ if (!strchr(HELPFILE, ':') && !strchr(HELPFILE, '/') && !strchr(HELPFILE, '\\')) { if (strlen(help_fname) == 0) { strcpy(help_fname, HELPFILE); if (shel_find(help_fname) == 0) { strcpy(help_fname, HELPFILE); } } help_ptr = help_fname; } else { help_ptr = HELPFILE; } # endif /* !SHELFIND */ /* Since MSDOS DGROUP segment is being overflowed we can not allow such */ /* huge static variables (1k each). Instead we dynamically allocate them */ /* on the first call to this function... */ if (helpbuf == NULL) { helpbuf = gp_alloc(MAX_LINE_LEN, "help buffer"); prompt = gp_alloc(MAX_LINE_LEN, "help prompt"); helpbuf[0] = prompt[0] = 0; } if (toplevel) helpbuf[0] = prompt[0] = 0; /* in case user hit ^c last time */ /* if called recursively, toplevel == 0; toplevel must == 1 if called * from command() to get the same behaviour as before when toplevel * supplied as function argument */ toplevel = 1; len = base = strlen(helpbuf); start = ++c_token; /* find the end of the help command */ while (!(END_OF_COMMAND)) c_token++; /* copy new help input into helpbuf */ if (len > 0) helpbuf[len++] = ' '; /* add a space */ capture(helpbuf + len, start, c_token - 1, MAX_LINE_LEN - len); squash_spaces(helpbuf + base); /* only bother with new stuff */ lower_case(helpbuf + base); /* only bother with new stuff */ len = strlen(helpbuf); /* now, a lone ? will print subtopics only */ if (strcmp(helpbuf + (base ? base + 1 : 0), "?") == 0) { /* subtopics only */ subtopics = 1; only = TRUE; helpbuf[base] = NUL; /* cut off question mark */ } else { /* normal help request */ subtopics = 0; only = FALSE; } switch (help(helpbuf, help_ptr, &subtopics)) { case H_FOUND:{ /* already printed the help info */ /* subtopics now is true if there were any subtopics */ screen_ok = FALSE; do { if (subtopics && !only) { /* prompt for subtopic with current help string */ if (len > 0) { strcpy (prompt, "Subtopic of "); strncat (prompt, helpbuf, MAX_LINE_LEN - 16); strcat (prompt, ": "); } else strcpy(prompt, "Help topic: "); read_line(prompt); num_tokens = scanner(&gp_input_line, &gp_input_line_len); c_token = 0; more_help = !(END_OF_COMMAND); if (more_help) { c_token--; toplevel = 0; /* base for next level is all of current helpbuf */ help_command(); } } else more_help = FALSE; } while (more_help); break; } case H_NOTFOUND: printf("Sorry, no help for '%s'\n", helpbuf); break; case H_ERROR: perror(help_ptr); break; default: int_error(NO_CARET, "Impossible case in switch"); break; } helpbuf[base] = NUL; /* cut it off where we started */ } #endif /* !NO_GIH */ #ifndef VMS static void do_system(const char *cmd) { # ifdef AMIGA_AC_5 static char *parms[80]; if (!cmd) return; getparms(input_line + 1, parms); fexecv(parms[0], parms); # elif defined(_Windows) && defined(USE_OWN_WINSYSTEM_FUNCTION) if (!cmd) return; winsystem(cmd); # else /* !(AMIGA_AC_5 || _Windows) */ /* (am, 19980929) * OS/2 related note: cmd.exe returns 255 if called w/o argument. * i.e. calling a shell by "!" will always end with an error message. * A workaround has to include checking for EMX,OS/2, two environment * variables,... */ if (!cmd) return; system(cmd); # endif /* !(AMIGA_AC_5 || _Windows) */ } # ifdef AMIGA_AC_5 /****************************************************************************** * Parses the command string (for fexecv use) and converts the first token * to lower case *****************************************************************************/ static void getparms(char *command, char **parms) { static char strg0[256]; int i = 0, j = 0, k = 0; /* A bunch of indices */ while (command[j] != NUL) { /* Loop on string characters */ parms[k++] = strg0 + i; while (command[j] == ' ') ++j; while (command[j] != ' ' && command[j] != NUL) { if (command[j] == '"') { /* Get quoted string */ do { strg0[i++] = command[j++]; } while (command[j] != '"' && command[j] != NUL); } strg0[i++] = command[j++]; } if (strg0[i] != NUL) strg0[i++] = NUL; /* NUL terminate every token */ } parms[k] = NUL; /* Convert to lower case */ /* FIXME HBB 20010621: do we really want to stop on char *before* * the actual end of the string strg0[]? */ for (k=0; strg0[k+1] != NUL; k++) if (strg0[k] >= 'A' && (strg0[k] <= 'Z')) strg0[k] += ('a' - 'A'); } # endif /* AMIGA_AC_5 */ # if defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE) /* keep some compilers happy */ static char *rlgets __PROTO((char *s, size_t n, const char *prompt)); static char * rlgets(char *s, size_t n, const char *prompt) { static char *line = (char *) NULL; static int leftover = -1; /* index of 1st char leftover from last call */ if (leftover == -1) { /* If we already have a line, first free it */ if (line != (char *) NULL) { free(line); line = NULL; /* so that ^C or int_error during readline() does * not result in line being free-ed twice */ } line = readline_ipc((interactive) ? prompt : ""); leftover = 0; /* If it's not an EOF */ if (line && *line) { #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE) int found; /* Initialize readline history functions */ using_history(); /* search in the history for entries containing line. * They may have other tokens before and after line, hence * the check on strcmp below. */ found = history_search(line, -1); if (found != -1 && !strcmp(current_history()->line,line)) { /* this line is already in the history, remove the earlier entry */ #if defined(HAVE_LIBREADLINE) HIST_ENTRY *removed = remove_history(where_history()); /* according to history docs we are supposed to free the stuff */ if (removed->line) free(removed->line); if (removed->data) free(removed->data); free(removed); #else remove_history(where_history()); #endif /* !HAVE_LIBREADLINE */ } add_history(line); # else /* !HAVE_LIBREADLINE && !HAVE_LIBEDITLINE */ add_history(line); # endif } } if (line) { /* s will be NUL-terminated here */ safe_strncpy(s, line + leftover, n); leftover += strlen(s); if (line[leftover] == NUL) leftover = -1; return s; } return NULL; } # endif /* READLINE || HAVE_LIBREADLINE */ # if defined(MSDOS) || defined(_Windows) || defined(DOS386) void do_shell() { screen_ok = FALSE; c_token++; if (user_shell) { # if defined(_Windows) if (WinExec(user_shell, SW_SHOWNORMAL) <= 32) # elif defined(DJGPP) if (system(user_shell) == -1) # else if (spawnl(P_WAIT, user_shell, NULL) == -1) # endif /* !(_Windows || DJGPP) */ os_error(NO_CARET, "unable to spawn shell"); } } # elif defined(AMIGA_SC_6_1) void do_shell() { screen_ok = FALSE; c_token++; if (user_shell) { if (system(user_shell)) os_error(NO_CARET, "system() failed"); } (void) putc('\n', stderr); } # elif defined(OS2) void do_shell() { screen_ok = FALSE; c_token++; if (user_shell) { if (system(user_shell) == -1) os_error(NO_CARET, "system() failed"); } (void) putc('\n', stderr); } # else /* !OS2 */ /* plain old Unix */ #define EXEC "exec " void do_shell() { static char exec[100] = EXEC; screen_ok = FALSE; c_token++; if (user_shell) { if (system(safe_strncpy(&exec[sizeof(EXEC) - 1], user_shell, sizeof(exec) - sizeof(EXEC) - 1))) os_error(NO_CARET, "system() failed"); } (void) putc('\n', stderr); } # endif /* !MSDOS */ /* read from stdin, everything except VMS */ # if !defined(READLINE) && !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDITLINE) # if (defined(MSDOS) || defined(DOS386)) && !defined(_Windows) && !defined(__EMX__) && !defined(DJGPP) /* if interactive use console IO so CED will work */ #define PUT_STRING(s) cputs(s) #define GET_STRING(s,l) ((interactive) ? cgets_emu(s,l) : fgets(s,l,stdin)) # ifdef __TURBOC__ /* cgets implemented using dos functions */ /* Maurice Castro 22/5/91 */ static char *doscgets __PROTO((char *)); static char * doscgets(char *s) { long datseg; /* protect and preserve segments - call dos to do the dirty work */ datseg = _DS; _DX = FP_OFF(s); _DS = FP_SEG(s); _AH = 0x0A; geninterrupt(33); _DS = datseg; /* check for a carriage return and then clobber it with a null */ if (s[s[1] + 2] == '\r') s[s[1] + 2] = 0; /* return the input string */ return (&(s[2])); } # endif /* __TURBOC__ */ # ifdef __ZTC__ void cputs(char *s) { int i = 0; while (s[i] != NUL) bdos(0x02, s[i++], NULL); } char * cgets(char *s) { bdosx(0x0A, s, NULL); if (s[s[1] + 2] == '\r') s[s[1] + 2] = 0; /* return the input string */ return (&(s[2])); } # endif /* __ZTC__ */ /* emulate a fgets like input function with DOS cgets */ char * cgets_emu(char *str, int len) { static char buffer[128] = ""; static int leftover = 0; if (buffer[leftover] == NUL) { buffer[0] = 126; # ifdef __TURBOC__ doscgets(buffer); # else cgets(buffer); # endif fputc('\n', stderr); if (buffer[2] == 26) return NULL; leftover = 2; } safe_strncpy(str, buffer + leftover, len); leftover += strlen(str); return str; } # else /* !plain DOS */ # define PUT_STRING(s) fputs(s, stderr) # define GET_STRING(s,l) fgets(s, l, stdin) # endif /* !plain DOS */ # endif /* !READLINE && !HAVE_LIBREADLINE) */ /* this function is called for non-interactive operation. Its usage is * like fgets(), but additionally it checks for ipc events from the * terminals waitforinput() (if USE_MOUSE, and terminal is * mouseable). This function will be used when reading from a pipe. * fgets() reads in at most one less than size characters from stream and * stores them into the buffer pointed to by s. * Reading stops after an EOF or a newline. If a newline is read, it is * stored into the buffer. A '\0' is stored after the last character in * the buffer. */ static char* fgets_ipc( char *dest, /* string to fill */ int len) /* size of it */ { #ifdef USE_MOUSE if (term && term->waitforinput) { /* This a mouseable terminal --- must expect input from it */ int c; /* char got from waitforinput() */ size_t i=0; /* position inside dest */ dest[0] = '\0'; for (i=0; i < len-1; i++) { c = term->waitforinput(); if ('\n' == c) { dest[i] = '\n'; i++; break; } else if (EOF == c) { dest[i] = '\0'; return (char*) 0; } else { dest[i] = c; } } dest[i] = '\0'; return dest; } else #endif return fgets(dest, len, stdin); } /* get a line from stdin, and display a prompt if interactive */ static char* gp_get_string(char * buffer, size_t len, const char * prompt) { # if defined(READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE) if (interactive) return rlgets(buffer, len, prompt); else return fgets_ipc(buffer, len); # else /* !(READLINE || HAVE_LIBREADLINE) */ if (interactive) PUT_STRING(prompt); return GET_STRING(buffer, len); # endif /* !(READLINE || HAVE_LIBREADLINE) */ } /* Non-VMS version */ static int read_line(const char *prompt) { int start = 0; TBOOLEAN more = FALSE; int last = 0; current_prompt = prompt; /* HBB NEW 20040727 */ do { /* grab some input */ if (gp_get_string(gp_input_line + start, gp_input_line_len - start, ((more) ? ">" : prompt)) == (char *) NULL) { /* end-of-file */ if (interactive) (void) putc('\n', stderr); gp_input_line[start] = NUL; inline_num++; if (start > 0) /* don't quit yet - process what we have */ more = FALSE; else return (1); /* exit gnuplot */ } else { /* normal line input */ /* gp_input_line must be NUL-terminated for strlen not to pass the * the bounds of this array */ last = strlen(gp_input_line) - 1; if (last >= 0) { if (gp_input_line[last] == '\n') { /* remove any newline */ gp_input_line[last] = NUL; if (last > 0 && gp_input_line[last-1] == '\r') { gp_input_line[--last] = NUL; } /* Watch out that we don't backup beyond 0 (1-1-1) */ if (last > 0) --last; } else if (last + 2 >= gp_input_line_len) { extend_input_line(); /* read rest of line, don't print "> " */ start = last + 1; more = TRUE; continue; /* else fall through to continuation handling */ } /* if(grow buffer?) */ if (gp_input_line[last] == '\\') { /* line continuation */ start = last; more = TRUE; } else more = FALSE; } else more = FALSE; } } while (more); return (0); } #endif /* !VMS */ #if defined(_Windows) # if defined(USE_OWN_WINSYSTEM_FUNCTION) /* there is a system like call on MS Windows but it is a bit difficult to use, so we will invoke the command interpreter and use it to execute the commands */ static int winsystem(const char *s) { LPSTR comspec; LPSTR execstr; LPCSTR p; /* get COMSPEC environment variable */ # ifdef WIN32 char envbuf[81]; GetEnvironmentVariable("COMSPEC", envbuf, 80); if (*envbuf == NUL) comspec = "\\command.com"; else comspec = envbuf; # else p = GetDOSEnvironment(); comspec = "\\command.com"; while (*p) { if (!strncmp(p, "COMSPEC=", 8)) { comspec = p + 8; break; } p += strlen(p) + 1; } # endif /* if the command is blank we must use command.com */ p = s; while ((*p == ' ') || (*p == '\n') || (*p == '\r')) p++; if (*p == NUL) { WinExec(comspec, SW_SHOWNORMAL); } else { /* attempt to run the windows/dos program via windows */ if (WinExec(s, SW_SHOWNORMAL) <= 32) { /* attempt to run it as a dos program from command line */ execstr = gp_alloc(strlen(s) + strlen(comspec) + 6, "winsystem cmdline"); strcpy(execstr, comspec); strcat(execstr, " /c "); strcat(execstr, s); WinExec(execstr, SW_SHOWNORMAL); free(execstr); } } /* regardless of the reality return OK - the consequences of */ /* failure include shutting down Windows */ return (0); /* success */ } # endif /* USE_OWN_WINSYSTEM_FUNCTION */ void call_kill_pending_Pause_dialog() { kill_pending_Pause_dialog(); } #endif /* _Windows */ #ifdef GP_MACROS /* * Walk through the input line looking for string variables preceded by @. * Replace the characters @ with the contents of the string. * Anything inside quotes is not expanded. */ #define COPY_CHAR gp_input_line[o++] = *c; \ after_backslash = FALSE; static int string_expand() { TBOOLEAN in_squote = FALSE; TBOOLEAN in_dquote = FALSE; TBOOLEAN after_backslash = FALSE; TBOOLEAN in_comment= FALSE; int len; int o = 0; int nfound = 0; char *c; char *temp_string; char temp_char; char *m; struct udvt_entry *udv; /* Most lines have no macros */ if (!strchr(gp_input_line,'@')) return(0); temp_string = gp_alloc(gp_input_line_len,"string variable"); len = strlen(gp_input_line); if (len >= gp_input_line_len) len = gp_input_line_len-1; strncpy(temp_string,gp_input_line,len); temp_string[len] = '\0'; for (c=temp_string; len && c && *c; c++, len--) { switch (*c) { case '@': /* The only tricky bit */ if (!in_squote && !in_dquote && !in_comment && isalpha(c[1])) { /* Isolate the udv key as a null-terminated substring */ m = ++c; while (isalnum(*c) || (*c=='_')) c++; temp_char = *c; *c = '\0'; /* Look up the key and restore the original following char */ udv = add_udv_by_name(m); if (udv && udv->udv_value.type == STRING) { nfound++; m = udv->udv_value.v.string_val; FPRINTF((stderr,"Replacing @%s with \"%s\"\n",udv->udv_name,m)); while (strlen(m) + o + len > gp_input_line_len) extend_input_line(); while (*m) gp_input_line[o++] = (*m++); } else { int_warn( NO_CARET, "%s is not a string variable",m); } *c-- = temp_char; } else COPY_CHAR; break; case '"': if (!after_backslash) in_dquote = !in_dquote; COPY_CHAR; break; case '\'': in_squote = !in_squote; COPY_CHAR; break; case '\\': if (in_dquote) after_backslash = !after_backslash; gp_input_line[o++] = *c; break; case '#': if (!in_squote && !in_dquote) in_comment = TRUE; default : COPY_CHAR; break; } } gp_input_line[o] = '\0'; free(temp_string); if (nfound) FPRINTF((stderr, "After string substitution command line is:\n\t%s\n", gp_input_line)); return(nfound); } #endif /* much more than what can be useful */ #define MAX_TOTAL_LINE_LEN (1024 * MAX_LINE_LEN) int do_system_func(const char *cmd, char **output) { #if defined(VMS) || defined(PIPES) int c; FILE *f; size_t cmd_len; int result_allocated, result_pos; char* result; int ierr = 0; # ifdef AMIGA_AC_5 int fd; # elif defined(VMS) int chan, one = 1; struct dsc$descriptor_s pgmdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; static $DESCRIPTOR(lognamedsc, "PLOT$MAILBOX"); # endif /* VMS */ cmd_len = strlen(cmd); /* open stream */ # ifdef VMS pgmdsc.dsc$a_pointer = cmd; pgmdsc.dsc$w_length = cmd_len; if (!((vaxc$errno = sys$crembx(0, &chan, 0, 0, 0, 0, &lognamedsc)) & 1)) os_error(NO_CARET, "sys$crembx failed"); if (!((vaxc$errno = lib$spawn(&pgmdsc, 0, &lognamedsc, &one)) & 1)) os_error(NO_CARET, "lib$spawn failed"); if ((f = fopen("PLOT$MAILBOX", "r")) == NULL) os_error(NO_CARET, "mailbox open failed"); # elif defined(AMIGA_AC_5) if ((fd = open(cmd, "O_RDONLY")) == -1) # else /* everyone else */ if ((f = popen(cmd, "r")) == NULL) os_error(NO_CARET, "popen failed"); # endif /* everyone else */ /* get output */ result_pos = 0; result_allocated = MAX_LINE_LEN; result = gp_alloc(MAX_LINE_LEN, "do_system_func"); result[0] = NUL; while (1) { # if defined(AMIGA_AC_5) char ch; if (read(fd, &ch, 1) != 1) break; c = ch; # else if ((c = getc(f)) == EOF) break; # endif /* !AMIGA_AC_5 */ /* result <- c */ result[result_pos++] = c; if ( result_pos == result_allocated ) { if ( result_pos >= MAX_TOTAL_LINE_LEN ) { result_pos--; int_warn(NO_CARET, "*very* long system call output has been truncated"); break; } else { result = gp_realloc(result, result_allocated + MAX_LINE_LEN, "extend in do_system_func"); result_allocated += MAX_LINE_LEN; } } } result[result_pos] = NUL; /* close stream */ # ifdef AMIGA_AC_5 (void) close(fd); # else /* Rest of the world */ ierr = pclose(f); # endif result = gp_realloc(result, strlen(result)+1, "do_system_func"); *output = result; return ierr; #else /* VMS || PIPES */ int_warn(NO_CARET, "system evaluation not supported by %s", OS); *output = gp_strdup(""); return 0; #endif /* VMS || PIPES */ }