GitHub Viewer
#ifndef lint
static char *RCSid() { return RCSid("$Id: command.c,v 1.38.2.3 2000/06/04 12:53:20 joze Exp $"); }
#endif
/* GNUPLOT - command.c */
/*[
* Copyright 1986 - 1993, 1998 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 "plot.h"
#include "alloc.h"
#include "command.h"
#include "eval.h"
#include "fit.h"
#include "binary.h"
#include "gp_hist.h"
#include "gp_time.h"
#include "misc.h"
#include "parse.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
/* GNU readline
* Only required by two files directly,
* so I don't put this into a header file. -lh
*/
#ifdef HAVE_LIBREADLINE
# include
# include
#endif
#if (defined(MSDOS) || defined(DOS386)) && defined(__TURBOC__) && !defined(_Windows)
unsigned _stklen = 16394; /* increase stack size */
#endif /* MSDOS && TURBOC */
#ifdef OS2
extern int PM_pause(char *); /* term/pm.trm */
extern int ExecuteMacro(char *, int); /* plot.c */
extern TBOOLEAN CallFromRexx; /* plot.c */
# ifdef USE_MOUSE
# 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 /* USE_MOUSE */
#endif /* OS2 */
/* always include ipc.h as it contains the prototype for readline_ipc */
#include "ipc.h"
#ifndef _Windows
# include "help.h"
#else
static int winsystem __PROTO((char *));
#endif /* _Windows */
#ifdef _Windows
# include
# ifdef __MSC__
# include
# else
# include
# include /* setdisk() */
# endif /* !MSC */
# include "win/wgnuplib.h"
extern TW textwin;
extern LPSTR winhelpname;
extern void screen_dump(void); /* in term/win.trm */
extern int Pause(LPSTR mess); /* in winmain.c */
#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));
#ifdef USE_MOUSE
static char* fgets_ipc __PROTO((char* dest, int len));
#endif
static int read_line __PROTO((const char *prompt));
static void do_system __PROTO((const char *));
#ifdef AMIGA_AC_5
static void getparms __PROTO((char *, char **));
#endif
struct lexical_unit *token;
int token_table_size;
char *input_line;
size_t input_line_len;
int inline_num; /* input line number */
struct udft_entry *dummy_func;
/* current dummy vars */
char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
/* support for replot command */
char *replot_line;
int plot_token; /* start of 'plot' command */
/* If last plot was a 3d one. */
TBOOLEAN is_3d_plot = FALSE;
/* 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 command_exit_status = 0;
/* support for dynamic size of input line */
void
extend_input_line()
{
if (input_line_len == 0) {
/* first time */
input_line = gp_alloc(MAX_LINE_LEN, "input_line");
input_line_len = MAX_LINE_LEN;
input_line[0] = NUL;
#if defined(USE_MOUSE) && defined(OS2)
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 /* USE_MOUSE && OS2 */
} else {
input_line = gp_realloc(input_line, input_line_len + MAX_LINE_LEN, "extend input line");
input_line_len += MAX_LINE_LEN;
FPRINTF((stderr, "extending input line to %d chars\n", input_line_len));
}
}
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));
}
}
#if defined(USE_MOUSE) && defined(OS2)
void thread_read_line()
{
thread_rl_Running = 1;
thread_rl_RetCode = ( read_line(PROMPT) );
thread_rl_Running = 0;
DosPostEventSem(semInputReady);
}
#endif /* USE_MOUSE && OS2 */
int
com_line()
{
#if defined(USE_MOUSE) && defined(OS2)
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 /* USE_MOUSE && OS2 */
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
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(input_line, input_line_SharedMem);
input_line_SharedMem[0] = 0;
thread_rl_RetCode = 0;
}
if (thread_rl_RetCode)
return (1);
#else
if (read_line(PROMPT))
return (1);
#endif /* OS2 */
#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 = input_line;
/* Skip leading whitespace */
while (isspace((int) *inlptr))
inlptr++;
if (inlptr != input_line) {
/* If there was leading whitespace, copy the actual
* command string to the front. use memmove() because
* source and target may overlap */
memmove(input_line, inlptr, strlen(inlptr));
/* Terminate resulting string */
input_line[strlen(inlptr)] = NUL;
}
FPRINTF((stderr, "Input line: \"%s\"\n", input_line));
/* also used in load_file */
if (is_system(input_line[0])) {
do_system(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(&input_line, &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);
}
#ifdef USE_MOUSE
void
toggle_display_of_ipc_commands(void)
{
if (mouse_setting.verbose)
mouse_setting.verbose = 0;
else
mouse_setting.verbose = 1;
}
int
display_ipc_commands(void)
{
return mouse_setting.verbose;
}
void
do_string(s)
char *s;
{
char *orig_input_line;
static char buf[256];
if (display_ipc_commands())
fprintf(stderr, "%s\n", s);
orig_input_line=input_line;
input_line=buf;
strcpy(buf,s);
do_line();
input_line=orig_input_line;
}
void
do_string_replot(s)
char *s;
{
char *orig_input_line;
static char buf[256];
orig_input_line = input_line;
input_line = buf;
strcpy(buf,s);
if (!replot_disabled)
strcat(buf,"; replot");
if (display_ipc_commands())
fprintf(stderr, "%s\n", buf);
do_line();
input_line = orig_input_line;
}
void
restore_prompt(void)
{
if (interactive) {
#if defined(HAVE_LIBREADLINE)
rl_forced_update_display();
#else
fputs(PROMPT, stderr);
fflush(stderr);
#endif
}
}
#endif /* USE_MOUSE */
void
define()
{
register int start_token; /* the 1st token in the function definition */
register struct udvt_entry *udv;
register struct udft_entry *udf;
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);
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((char *) 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 */
} else {
/* variable ! */
start_token = c_token;
c_token += 2;
udv = add_udv(start_token);
(void) const_express(&(udv->udv_value));
udv->udv_undef = FALSE;
}
}
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;
}
#ifdef USE_MOUSE
#define WHITE_AFTER_TOKEN(x) \
(' ' == input_line[token[x].start_index + token[x].length] \
|| '\t' == input_line[token[x].start_index + token[x].length] \
|| '\0' == input_line[token[x].start_index + token[x].length])
/* process the 'bind' command */
void
bind_command()
{
char* lhs = (char*) 0;
char* rhs = (char*) 0;
++c_token;
if (!END_OF_COMMAND && equals(c_token,"!")) {
bind_remove_all();
++c_token;
return;
}
/* get left hand side: the key or key sequence */
if (!END_OF_COMMAND) {
char* first = 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 input_line is too big, but shouldn't hurt too much. */
if (!END_OF_COMMAND) {
rhs = (char*) gp_alloc(strlen(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));
#if 0
if (!END_OF_COMMAND) {
int_error(c_token, "usage: bind \"\" \"\"");
}
#endif
/* bind_process() will eventually free lhs / rhs ! */
bind_process(lhs, rhs);
}
#endif /* USE_MOUSE */
/*
* Command parser functions
*/
/* process the 'call' command */
void
call_command()
{
char *save_file = NULL;
if (!isstring(++c_token))
int_error(c_token, "expecting filename");
else {
m_quote_capture(&save_file, c_token, c_token);
gp_expand_tilde(&save_file);
/* Argument list follows filename */
load_file(loadpath_fopen(save_file, "r"), save_file, TRUE);
/* input_line[] and token[] now destroyed! */
c_token = 0;
num_tokens = 0;
free(save_file);
}
}
/* process the 'cd' command */
void
changedir_command()
{
char *save_file = NULL;
if (!isstring(++c_token))
int_error(c_token, "expecting directory name");
else {
m_quote_capture(&save_file, c_token, c_token);
gp_expand_tilde(&save_file);
if (changedir(save_file)) {
int_error(c_token, "Can't change to this directory");
}
c_token++;
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 'exit' and 'quit' commands */
void
exit_command()
{
/* 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)
struct value a;
char *name = NULL; /* name of the output file; NULL for stdout */
int n = 0; /* print only entries */
c_token++;
if (!END_OF_COMMAND && equals(c_token,"?")) {
/* find and show the entries */
c_token++;
m_capture(&name, c_token, c_token);
printf ("history ?%s\n", name);
if (!history_find_all(name))
int_error(c_token,"not in history");
c_token++;
} else if (!END_OF_COMMAND && equals(c_token,"!")) {
/* execute the entry */
static char flag = 0;
static char *save_input_line;
static int save_c_token, save_input_line_len;
if (flag) {
flag = 0;
input_line = save_input_line;
c_token = save_c_token;
input_line_len = save_input_line_len;
num_tokens = scanner(&input_line, &input_line_len);
int_error(c_token,"recurrency forbidden");
}
c_token++;
m_capture(&name, c_token, c_token);
name = history_find(name);
if (name == NULL)
int_error(c_token,"not in history");
else {
/* execute the command "name" */
save_input_line = input_line;
save_c_token = c_token;
save_input_line_len = input_line_len;
flag = 1;
input_line = name;
printf(" Executing:\n\t%s\n",name);
do_line();
input_line = save_input_line;
c_token = save_c_token;
input_line_len = save_input_line_len;
num_tokens = scanner(&input_line, &input_line_len);
flag = 0;
}
c_token++;
} else {
/* show history entries */
if (!END_OF_COMMAND && isanumber(c_token)) {
n = (int)real(const_express(&a));
}
if (!END_OF_COMMAND && isstring(c_token)) {
m_quote_capture(&name, c_token, c_token);
c_token++;
}
write_history_n(n, name);
}
#else
c_token++;
int_warn(NO_CARET, "history command requires some form of readline support");
#endif /* READLINE && !HAVE_LIBREADLINE */
}
#define REPLACE_ELSE(tok) \
do { \
int idx = token[tok].start_index; \
token[tok].length = 1; \
input_line[idx++] = ';'; /* e */ \
input_line[idx++] = ' '; /* l */ \
input_line[idx++] = ' '; /* s */ \
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 && input_line[i]; i++) { \
fputc(input_line[i], stderr); \
} \
fputc('\n', stderr); \
fflush(stderr); \
} while (0)
#endif
/* process the 'if' command */
void
if_command()
{
double exprval;
struct value t;
if_depth++;
if (!equals(++c_token, "(")) /* no expression */
int_error(c_token, "expecting (expression)");
exprval = real(const_express(&t));
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;
input_line[eolpos + 2] = ';';
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)
#elif defined(OS2)
if (strcmp(term->name, "pm") != 0 || sleep_time >= 0)
#elif defined(MTOS)
if (strcmp(term->name, "mtos") != 0 || sleep_time >= 0)
#endif /* _Windows */
fputs(buf, stderr);
text = 1;
}
}
if (sleep_time < 0) {
#ifdef _Windows
if (!Pause(buf)) {
free(buf);
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() ...
*/
free(buf);
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(sleep_time);
#elif defined(MTOS)
if (strcmp(term->name, "mtos") == 0) {
int MTOS_pause(char *buf);
int rc;
if ((rc = MTOS_pause(buf)) == 0)
free(buf);
bail_to_command_line();
else if (rc == 2) {
fputs(buf, stderr);
text = 1;
(void) fgets(buf, strlen(buf), stdin);
}
} else if (strcmp(term->name, "atari") == 0) {
char *line = readline("");
if (line)
free(line);
} else
(void) fgets(buf, strlen(buf), stdin);
#elif defined(ATARI)
if (strcmp(term->name, "atari") == 0) {
char *line = readline("");
if (line)
free(line);
} else
(void) fgets(buf, strlen(buf), stdin);
#else /* !(_Windows || OS2 || _Macintosh || MTOS || ATARI) */
#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 || MTOS || ATARI) */
}
if (sleep_time > 0)
GP_SLEEP(sleep_time);
if (text != 0 && sleep_time >= 0)
fputc('\n', stderr);
screen_ok = FALSE;
free(buf);
}
/* process the 'plot' command */
void
plot_command()
{
plot_token = c_token++;
plotted_data_from_stdin = 0;
SET_CURSOR_WAIT;
#ifdef USE_MOUSE
plot_mode(MODE_PLOT);
#endif
plotrequest();
SET_CURSOR_ARROW;
}
/* process the 'print' command */
void
print_command()
{
struct value a;
char *s;
/* space printed between two expressions only */
int need_space = 0;
screen_ok = FALSE;
do {
++c_token;
if (isstring(c_token)) {
s = NULL;
m_quote_capture(&s, c_token, c_token);
fputs(s, stderr);
need_space = 0;
free(s);
++c_token;
} else {
(void) const_express(&a);
if (need_space)
putc(' ', stderr);
need_space = 1;
disp_value(stderr, &a);
}
} while (!END_OF_COMMAND && equals(c_token, ","));
(void) putc('\n', stderr);
}
/* 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++;
}
/* 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 = 0;
#if 1
bail_to_command_line(); /* be silent --- don't mess the screen */
#else
int_error(c_token, "cannot replot data coming from stdin");
#endif
}
c_token++;
SET_CURSOR_WAIT;
replotrequest();
SET_CURSOR_ARROW;
}
/* process the 'reread' command */
void
reread_command()
{
FILE *fp = lf_top();
if (fp != (FILE *) NULL)
rewind(fp);
c_token++;
}
/* reset_command() is in set.c */
/* process the 'save' command */
void
save_command()
{
FILE *fp;
char *save_file = NULL;
c_token++;
switch(lookup_table(&save_tbl[0],c_token)) {
case SAVE_FUNCS:
if (!isstring(++c_token))
int_error(c_token, "expecting filename");
else {
CAPTURE_FILENAME_AND_FOPEN("w");
save_functions(fp);
}
break;
case SAVE_SET:
if (!isstring(++c_token))
int_error(c_token, "expecting filename");
else {
CAPTURE_FILENAME_AND_FOPEN("w");
save_set(fp);
}
break;
case SAVE_TERMINAL:
if (!isstring(++c_token))
int_error(c_token, "expecting filename");
else {
CAPTURE_FILENAME_AND_FOPEN("w");
save_term(fp);
}
break;
case SAVE_VARS:
if (!isstring(++c_token))
int_error(c_token, "expecting filename");
else {
CAPTURE_FILENAME_AND_FOPEN("w");
save_variables(fp);
}
break;
default:
if (isstring(c_token)) {
CAPTURE_FILENAME_AND_FOPEN("w");
save_all(fp);
} else
int_error(c_token, "filename or keyword 'functions', 'variables', 'terminal' or 'set' expected");
break;
}
c_token++;
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 = 0;
SET_CURSOR_WAIT;
#ifdef USE_MOUSE
plot_mode(MODE_SPLOT);
#endif
plot3drequest();
SET_CURSOR_ARROW;
}
/* process the 'system' command */
void
system_command()
{
if (!isstring(++c_token))
int_error(c_token, "expecting command");
else {
char *e = input_line + token[c_token].start_index + token[c_token].length - 1;
char c = *e;
*e = NUL;
do_system(input_line + token[c_token].start_index + 1);
*e = c;
}
c_token++;
}
/* 'test' command is processed by test_term() in term.c */
/* process the 'testtime' command */
void
testtime_command()
{
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 */
}
/* 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;
if (!isstring(++c_token))
int_error(c_token, "Parameter filename expected");
m_quote_capture(&opfname, c_token, c_token);
++c_token;
if (!(END_OF_COMMAND)) {
if (!isstring(c_token))
int_error(c_token, "New parameter filename expected");
else {
m_quote_capture(&npfname, c_token, c_token);
++c_token;
}
}
update(opfname, npfname);
free(npfname);
free(opfname);
}
/* process invalid commands and, on OS/2, REXX commands */
void
invalid_command()
{
#ifdef OS2
if (_osmode == OS2_MODE) {
if (token[c_token].is_token) {
int rc;
rc = ExecuteMacro(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(path)
char *path;
{
#if defined(MSDOS) || defined(WIN16) || defined(ATARI) || 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(ATARI)
(void) Dsetdrv(driveno);
# endif
# 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, ATARI 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 = &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(input_line,rest_args,rest_len+1);
/* reallocs if necessary */
while (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(input_line+replot_len,input_line,rest_len+1);
/* copy previous plot command to start of input line */
strncpy(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 + 2;
m_capture(&replot_args, c_token, last_token); /* might be empty */
while (input_line_len < newlen)
extend_input_line();
strcpy(input_line, replot_line);
strcat(input_line, ",");
strcat(input_line, replot_args);
free(replot_args);
}
plot_token = 0; /* whole line to be saved as replot line */
screen_ok = FALSE;
num_tokens = scanner(&input_line, &input_line_len);
c_token = 1; /* skip the 'plot' part */
if (is_3d_plot)
plot3drequest();
else
plotrequest();
}
/* 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(status)
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(prompt)
const char *prompt;
{
int more, start = 0;
char expand_prompt[40];
prompt_desc.dsc$w_length = strlen(prompt);
prompt_desc.dsc$a_pointer = (char *)prompt;
(void) strcpy(expand_prompt, "_");
(void) strncat(expand_prompt, prompt, 38);
do {
line_desc.dsc$w_length = MAX_LINE_LEN - start;
line_desc.dsc$a_pointer = &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;
input_line[start] = NUL;
inline_num++;
if (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(input_line);
line_desc.dsc$a_pointer = 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(cmd)
const char *cmd;
{
if (!cmd)
return;
/* 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 _Windows
# ifdef NO_GIH
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);
}
}
# endif /* NO_GIH */
#endif /* _Windows */
/*
* 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 defined(ATARI) || defined(MTOS)
char const *const ext[] = { NULL };
# 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
# if defined(ATARI) || defined(MTOS)
{
/* I hope findfile really can accept a NULL argument ... */
if ((help_ptr = findfile(HELPFILE, user_gnuplotpath, ext)) == NULL)
help_ptr = findfile(HELPFILE, getenv("PATH"), ext);
if (!help_ptr)
help_ptr = HELPFILE;
}
# else
help_ptr = HELPFILE;
# endif /* ATARI || MTOS */
# endif /* __TURBOC__ */
/* 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(&input_line, &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(cmd)
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(ATARI) && defined(__GNUC__))
/* || (defined(MTOS) && defined(__GNUC__)) */
/* use preloaded shell, if available */
short (*shell_p) (char *command);
void *ssp;
if (!cmd)
return;
ssp = (void *) Super(NULL);
shell_p = *(short (**)(char *)) 0x4f6;
Super(ssp);
/* this is a bit strange, but we have to have a single if */
if (shell_p)
(*shell_p) (cmd);
else
system(cmd);
# elif defined(_Windows)
if (!cmd)
return;
winsystem(cmd);
# else /* !(AMIGA_AC_5 || ATARI && __GNUC__ || _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 || ATARI&&__GNUC__ || _Windows) */
}
# ifdef AMIGA_AC_5
/******************************************************************************
* Parses the command string (for fexecv use) and converts the first token
* to lower case
*****************************************************************************/
static void
getparms(command, parms)
char *command;
char **parms;
{
static char strg0[256];
register 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 */
for (*(strg0 + (i++)) = *(command + (j++));
*(command + j) != '"';
*(strg0 + (i++)) = *(command + (j++)));
*(strg0 + (i++)) = *(command + (j++));
}
*(strg0 + (i++)) = NUL; /* NUL terminate every token */
}
parms[k] = NUL;
for (k = strlen(strg0) - 1; k >= 0; --k) /* Convert to lower case */
*(strg0 + k) >= 'A' && *(strg0 + k) waitforinput) {
char* ptr = dest;
char* end = dest + len;
*dest = '\0';
do {
*ptr = term->waitforinput();
if ('\n' == *ptr) {
*ptr = '\0';
return dest;
} else if (EOF == *ptr) {
return (char*) 0;
}
ptr++;
} while (ptr < end);
return dest;
} else {
return fgets(dest, len, stdin);
}
}
#endif
/* Non-VMS version */
static int
read_line(prompt)
const char *prompt;
{
int start = 0;
TBOOLEAN more = FALSE;
int last = 0;
# if !defined(READLINE) && !defined(HAVE_LIBREADLINE)
if (interactive)
PUT_STRING(prompt);
# endif /* no READLINE */
do {
/* grab some input */
# if defined(READLINE) || defined(HAVE_LIBREADLINE)
if (((interactive)
? rlgets(&(input_line[start]), input_line_len - start,
((more) ? "> " : prompt)) :
#ifdef USE_MOUSE
fgets_ipc(&(input_line[start]), input_line_len - start)
#else
fgets(&(input_line[start]), input_line_len - start, stdin)
#endif
) == (char *) NULL)
# else /* !(READLINE || HAVE_LIBREADLINE) */
if (GET_STRING(&(input_line[start]), input_line_len - start)
== (char *) NULL)
# endif /* !(READLINE || HAVE_LIBREADLINE) */
{
/* end-of-file */
if (interactive)
(void) putc('\n', stderr);
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 */
last = strlen(input_line) - 1;
if (last >= 0) {
if (input_line[last] == '\n') { /* remove any newline */
input_line[last] = NUL;
/* Watch out that we don't backup beyond 0 (1-1-1) */
if (last > 0)
--last;
} else if (last + 2 >= input_line_len) {
extend_input_line();
start = last + 1;
more = TRUE;
continue; /* read rest of line, don't print "> " */
}
if (input_line[last] == '\\') { /* line continuation */
start = last;
more = TRUE;
} else
more = FALSE;
} else
more = FALSE;
}
# if !defined(READLINE) && !defined(HAVE_LIBREADLINE)
if (more && interactive)
PUT_STRING("> ");
# endif
} while (more);
return (0);
}
#endif /* !VMS */
#ifdef _Windows
/* 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(char *s)
{
LPSTR comspec;
LPSTR execstr;
LPSTR 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)