home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 334_01 / command.c < prev    next >
Text File  |  1991-02-05  |  40KB  |  1,484 lines

  1. /* GNUPLOT - command.c */
  2. /*
  3.  * Copyright (C) 1986, 1987, 1990   Thomas Williams, Colin Kelley
  4.  *
  5.  * Permission to use, copy, and distribute this software and its
  6.  * documentation for any purpose with or without fee is hereby granted, 
  7.  * provided that the above copyright notice appear in all copies and 
  8.  * that both that copyright notice and this permission notice appear 
  9.  * in supporting documentation.
  10.  *
  11.  * Permission to modify the software is granted, but not the right to
  12.  * distribute the modified code.  Modifications are to be distributed 
  13.  * as patches to released version.
  14.  *  
  15.  * This software  is provided "as is" without express or implied warranty.
  16.  * 
  17.  *
  18.  * AUTHORS
  19.  * 
  20.  *   Original Software:
  21.  *     Thomas Williams,  Colin Kelley.
  22.  * 
  23.  *   Gnuplot 2.0 additions:
  24.  *       Russell Lang, Dave Kotz, John Campbell.
  25.  * 
  26.  * send your comments or suggestions to (pixar!info-gnuplot@sun.com).
  27.  * 
  28.  */
  29.  
  30. #include <stdio.h>
  31. #include <math.h>
  32.  
  33. #ifdef MSDOS
  34. #include <process.h>
  35.  
  36. #ifdef __ZTC__
  37. #define P_WAIT 0
  38. #include <time.h>    /* usleep() */
  39. #else
  40.  
  41. #ifdef __TURBOC__
  42. #include <dos.h>    /* sleep() */
  43. #include <conio.h>
  44. extern unsigned _stklen = 8192;    /* increase stack size */
  45.  
  46. #else    /* must be MSC */
  47. #include <time.h>    /* kludge to provide sleep() */
  48. void sleep();        /* defined later */
  49. #endif /* TURBOC */
  50. #endif /* ZTC */
  51.  
  52. #endif /* MSDOS */
  53.  
  54.  
  55. #include "plot.h"
  56. #include "setshow.h"
  57. #include "help.h"
  58.  
  59. #ifndef STDOUT
  60. #define STDOUT 1
  61. #endif
  62.  
  63. #ifndef HELPFILE
  64. #define HELPFILE "docs/gnuplot.gih" /* changed by makefile */
  65. #endif
  66.  
  67. #define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
  68.  
  69. /*
  70.  * instead of <strings.h>
  71.  */
  72.  
  73. extern char *gets(),*getenv();
  74. extern char *strcpy(),*strncpy(),*strcat();
  75. extern int strlen(), strcmp();
  76.  
  77. #ifdef unix
  78. #ifdef GETCWD
  79. extern char *getcwd();    /* some Unix's use getcwd */
  80. #else
  81. extern char *getwd();    /* most Unix's use getwd */
  82. #endif
  83. #else
  84. extern char *getcwd();    /* Turbo C, MSC and VMS use getcwd */
  85. #endif
  86. extern int chdir();
  87.  
  88. extern double magnitude(),angle(),real(),imag();
  89. extern struct value *const_express(), *pop(), *complex();
  90. extern struct at_type *temp_at(), *perm_at();
  91. extern struct udft_entry *add_udf();
  92. extern struct udvt_entry *add_udv();
  93. extern void squash_spaces();
  94. extern void lower_case();
  95.  
  96. extern BOOLEAN interactive;    /* from plot.c */
  97.  
  98. /* input data, parsing variables */
  99. struct lexical_unit token[MAX_TOKENS];
  100. char input_line[MAX_LINE_LEN+1] = "";
  101. int num_tokens, c_token;
  102. int inline_num = 0;            /* input line number */
  103.  
  104. char c_dummy_var[MAX_ID_LEN+1];         /* current dummy var */
  105.  
  106. /* the curves of the plot */
  107. struct curve_points *first_plot = NULL;
  108. static struct udft_entry plot_func;
  109. struct udft_entry *dummy_func;
  110.  
  111. /* support for replot command */
  112. char replot_line[MAX_LINE_LEN+1] = "";
  113. static int plot_token;                    /* start of 'plot' command */
  114.  
  115. com_line()
  116. {
  117.     read_line(PROMPT);
  118.  
  119.     /* So we can flag any new output: if false at time of error, */
  120.     /* we reprint the command line before printing caret. */
  121.     /* TRUE for interactive terminals, since the command line is typed. */
  122.     /* FALSE for non-terminal stdin, so command line is printed anyway. */
  123.     /* (DFK 11/89) */
  124.     screen_ok = interactive; 
  125.  
  126.     do_line();
  127. }
  128.  
  129.  
  130. do_line()      /* also used in load_file */
  131. {
  132.     if (is_system(input_line[0])) {
  133.         do_system();
  134.         (void) fputs("!\n",stderr);
  135.         return;
  136.     }
  137.     num_tokens = scanner(input_line);
  138.     c_token = 0;
  139.     while(c_token < num_tokens) {
  140.         command();
  141.         if (c_token < num_tokens)    /* something after command */
  142.             if (equals(c_token,";"))
  143.                 c_token++;
  144.             else
  145.                     int_error("';' expected",c_token);
  146.     }
  147. }
  148.  
  149.  
  150.  
  151. command()
  152. {
  153.     char sv_file[MAX_LINE_LEN+1];
  154.     /* string holding name of save or load file */
  155.  
  156.     c_dummy_var[0] = '\0';        /* no dummy variable */
  157.  
  158.     if (is_definition(c_token))
  159.         define();
  160.     else if (equals(c_token,"help") || equals(c_token,"?")) {
  161.         c_token++;
  162.         do_help();
  163.     }
  164.     else if (almost_equals(c_token,"test")) {
  165.         c_token++;
  166.         test_term();
  167.     }
  168.     else if (almost_equals(c_token,"pa$use")) {
  169.         struct value a;
  170.         int stime, text=0;
  171.         char buf[MAX_LINE_LEN+1];
  172.  
  173.         c_token++;
  174.         stime = (int )real(const_express(&a));
  175.         if (!(END_OF_COMMAND)) {
  176.             if (!isstring(c_token))
  177.                 int_error("expecting string",c_token);
  178.             else {
  179.                 quotel_str(buf,c_token);
  180.                 (void) fprintf (stderr, "%s",buf);
  181.                 text = 1;
  182.             }
  183.         }
  184.         if (stime < 0) (void) fgets (buf,MAX_LINE_LEN,stdin);  
  185.                         /* Hold until CR hit. */
  186. #ifdef __ZTC__
  187.         if (stime > 0) usleep((unsigned long) stime);
  188. #else
  189.         if (stime > 0) sleep((unsigned int) stime);
  190. #endif
  191.         if (text != 0 && stime >= 0) (void) fprintf (stderr,"\n");
  192.         c_token++;
  193.         screen_ok = FALSE;
  194.     }
  195.     else if (almost_equals(c_token,"pr$int")) {
  196.         struct value a;
  197.  
  198.         c_token++;
  199.         (void) const_express(&a);
  200.         (void) putc('\t',stderr);
  201.         disp_value(stderr,&a);
  202.         (void) putc('\n',stderr);
  203.         screen_ok = FALSE;
  204.     }
  205.     else if (almost_equals(c_token,"p$lot")) {
  206.         plot_token = c_token++;
  207.         plotrequest();
  208.     }
  209.     else if (almost_equals(c_token,"rep$lot")) {
  210.         if (replot_line[0] == '\0') 
  211.             int_error("no previous plot",c_token);
  212.         c_token++;
  213.         replotrequest();
  214.     }
  215.     else if (almost_equals(c_token,"se$t"))
  216.         set_command();
  217.     else if (almost_equals(c_token,"sh$ow"))
  218.         show_command();
  219.     else if (almost_equals(c_token,"cl$ear")) {
  220.         if (!term_init) {
  221.             (*term_tbl[term].init)();
  222.             term_init = TRUE;
  223.         }
  224.         (*term_tbl[term].graphics)();
  225.         (*term_tbl[term].text)();
  226.         (void) fflush(outfile);
  227.         screen_ok = FALSE;
  228.         c_token++;
  229.     }
  230.     else if (almost_equals(c_token,"she$ll")) {
  231.         do_shell();
  232.         screen_ok = FALSE;
  233.         c_token++;
  234.     }
  235.     else if (almost_equals(c_token,"sa$ve")) {
  236.         if (almost_equals(++c_token,"f$unctions")) {
  237.             if (!isstring(++c_token))
  238.                 int_error("expecting filename",c_token);
  239.             else {
  240.                 quote_str(sv_file,c_token);
  241.                 save_functions(fopen(sv_file,"w"));
  242.             }
  243.         }
  244.         else if (almost_equals(c_token,"v$ariables")) {
  245.             if (!isstring(++c_token))
  246.                 int_error("expecting filename",c_token);
  247.             else {
  248.                 quote_str(sv_file,c_token);
  249.                 save_variables(fopen(sv_file,"w"));
  250.             }
  251.         }
  252.         else if (almost_equals(c_token,"s$et")) {
  253.             if (!isstring(++c_token))
  254.                 int_error("expecting filename",c_token);
  255.             else {
  256.                 quote_str(sv_file,c_token);
  257.                 save_set(fopen(sv_file,"w"));
  258.             }
  259.         }
  260.         else if (isstring(c_token)) {
  261.             quote_str(sv_file,c_token);
  262.             save_all(fopen(sv_file,"w"));
  263.         }
  264.         else {
  265.             int_error(
  266.         "filename or keyword 'functions', 'variables', or 'set' expected",
  267.                     c_token);
  268.         }
  269.         c_token++;
  270.     }
  271.     else if (almost_equals(c_token,"l$oad")) {
  272.         if (!isstring(++c_token))
  273.             int_error("expecting filename",c_token);
  274.         else {
  275.             quote_str(sv_file,c_token);
  276.             load_file(fopen(sv_file,"r"), sv_file);    
  277.         /* input_line[] and token[] now destroyed! */
  278.             c_token = num_tokens = 0;
  279.         }
  280.     }
  281.     else if (almost_equals(c_token,"cd")) {
  282.         if (!isstring(++c_token))
  283.             int_error("expecting directory name",c_token);
  284.         else {
  285.             quotel_str(sv_file,c_token);
  286.             if (chdir(sv_file)) {
  287.               int_error("Can't change to this directory",c_token);
  288.             }
  289.         c_token++;
  290.         }
  291.     }
  292.     else if (almost_equals(c_token,"pwd")) {
  293. #ifdef unix
  294. #ifdef GETCWD
  295.       (void) getcwd(sv_file,MAX_ID_LEN); /* some Unix's use getcwd */
  296. #else
  297.       (void) getwd(sv_file); /* most Unix's use getwd */
  298. #endif
  299. #else
  300. /* Turbo C and VMS have getcwd() */
  301.       (void) getcwd(sv_file,MAX_ID_LEN);
  302. #endif
  303.       fprintf(stderr,"%s\n", sv_file);
  304.       c_token++;
  305.     }
  306.     else if (almost_equals(c_token,"ex$it") ||
  307.             almost_equals(c_token,"q$uit")) {
  308.         done(IO_SUCCESS);
  309.     }
  310.     else if (!equals(c_token,";")) {        /* null statement */
  311.         int_error("invalid command",c_token);
  312.     }
  313. }
  314.  
  315. replotrequest()
  316. {
  317. char str[MAX_LINE_LEN+1];
  318.         if(equals(c_token,"["))
  319.             int_error("cannot set range with replot",c_token);
  320.         if (!END_OF_COMMAND) {
  321.             capture(str,c_token,num_tokens-1);
  322.             if ( (strlen(str) + strlen(replot_line)) <= MAX_LINE_LEN-1) {
  323.                 (void) strcat(replot_line,",");
  324.                 (void) strcat(replot_line,str); 
  325.             } else {
  326.                 int_error("plot line too long with replot arguments",c_token);
  327.             }
  328.         }
  329.         (void) strcpy(input_line,replot_line);
  330.         screen_ok = FALSE;
  331.         num_tokens = scanner(input_line);
  332.         c_token = 1;                    /* skip the 'plot' part */
  333.         plotrequest();
  334. }
  335.  
  336.  
  337. plotrequest()
  338. /*
  339.    In the parametric case we can say 
  340.       plot [a= -4:4] [-2:2] [-1:1] sin(a),a**2
  341.    while in the non-parametric case we would say only
  342.       plot [b= -2:2] [-1:1] sin(b)
  343. */
  344. {
  345.      BOOLEAN changed;
  346.     int dummy_token = -1;
  347.  
  348.     autoscale_lt = autoscale_t;
  349.      autoscale_lx = autoscale_x;
  350.      autoscale_ly = autoscale_y;
  351.     
  352.     if (!term)                    /* unknown */
  353.         int_error("use 'set term' to set terminal type first",c_token);
  354.  
  355.     if (equals(c_token,"[")) {
  356.         c_token++;
  357.         if (isletter(c_token)) {
  358.             if (equals(c_token+1,"=")) {
  359.                dummy_token = c_token;
  360.                c_token += 2;
  361.             } else {
  362.                /* oops; probably an expression with a variable. */
  363.                /* Parse it as an xmin expression. */
  364.                /* used to be: int_error("'=' expected",c_token); */
  365.             }
  366.         }
  367.         changed = parametric ? load_range(&tmin,&tmax):load_range(&xmin,&xmax);
  368.         if (!equals(c_token,"]"))
  369.             int_error("']' expected",c_token);
  370.         c_token++;
  371.         if (changed) {
  372.             if (parametric)
  373.                 autoscale_lt = FALSE;
  374.             else
  375.                 autoscale_lx = FALSE;
  376.         }
  377.     }
  378.  
  379.     if (parametric && equals(c_token,"[")) { /* set optional x ranges */
  380.         c_token++;
  381.         changed = load_range(&xmin,&xmax);
  382.         if (!equals(c_token,"]"))
  383.             int_error("']' expected",c_token);
  384.         c_token++;
  385.         if (changed)
  386.           autoscale_lx = FALSE;
  387.     }
  388.  
  389.     if (equals(c_token,"[")) { /* set optional y ranges */
  390.         c_token++;
  391.         changed = load_range(&ymin,&ymax);
  392.         if (!equals(c_token,"]"))
  393.             int_error("']' expected",c_token);
  394.         c_token++;
  395.         if (changed)
  396.           autoscale_ly = FALSE;
  397.     }
  398.  
  399.      /* use the default dummy variable unless changed */
  400.     if (dummy_token >= 0)
  401.       copy_str(c_dummy_var,dummy_token);
  402.     else
  403.       (void) strcpy(c_dummy_var,dummy_var);
  404.  
  405.     eval_plots();
  406. }
  407.  
  408.  
  409. define()
  410. {
  411. register int start_token;  /* the 1st token in the function definition */
  412. register struct udvt_entry *udv;
  413. register struct udft_entry *udf;
  414.  
  415.     if (equals(c_token+1,"(")) {
  416.         /* function ! */
  417.         start_token = c_token;
  418.         copy_str(c_dummy_var, c_token + 2);
  419.         c_token += 5; /* skip (, dummy, ) and = */
  420.         if (END_OF_COMMAND)
  421.             int_error("function definition expected",c_token);
  422.         udf = dummy_func = add_udf(start_token);
  423.         if (udf->at)                /* already a dynamic a.t. there */
  424.             free((char *)udf->at);    /* so free it first */
  425.         if ((udf->at = perm_at()) == (struct at_type *)NULL)
  426.             int_error("not enough memory for function",start_token);
  427.         m_capture(&(udf->definition),start_token,c_token-1);
  428.     }
  429.     else {
  430.         /* variable ! */
  431.         start_token = c_token;
  432.         c_token +=2;
  433.         udv = add_udv(start_token);
  434.         (void) const_express(&(udv->udv_value));
  435.         udv->udv_undef = FALSE;
  436.     }
  437. }
  438.  
  439.  
  440. get_data(this_plot)
  441. struct curve_points *this_plot;
  442. {
  443. static char data_file[MAX_LINE_LEN+1], line[MAX_LINE_LEN+1];
  444. register int i, l_num, datum;
  445. register FILE *fp;
  446. float x, y;
  447. int npoints;                /* number of points and breaks read */
  448. float temp;
  449. BOOLEAN yfirst;
  450. char format[MAX_LINE_LEN+1];
  451.  
  452.     quote_str(data_file, c_token);
  453.     this_plot->plot_type = DATA;
  454.     if ((fp = fopen(data_file, "r")) == (FILE *)NULL)
  455.         os_error("can't open data file", c_token);
  456.  
  457.     format[0] = '\0';
  458.     yfirst = FALSE;
  459.     c_token++;    /* skip data file name */
  460.     if (almost_equals(c_token,"u$sing")) {
  461.         c_token++;      /* skip "using" */
  462.         if (!isstring(c_token)) {
  463.             if ( equals(c_token,"xy") || equals(c_token,"y") )
  464.                 yfirst = FALSE;
  465.             else if (equals(c_token,"yx"))
  466.                 yfirst = TRUE;
  467.             else 
  468.                 int_error("expecting xy, yx, y or format (scanf) string",c_token);
  469.             c_token++;    /* skip "xy", "yx" or "y" */
  470.         }
  471.         if (isstring(c_token)) {
  472.             quotel_str(format, c_token);
  473.             c_token++;    /* skip format */
  474.         }
  475.         else
  476.             int_error("expecting format (scanf) string",c_token);
  477.     }
  478.     if (strlen(format) == 0)
  479.         strcpy(format,"%f %f");
  480.  
  481.     l_num = 0;
  482.      datum = 0;
  483.     i = 0;
  484.      npoints = 0;
  485.     while ( fgets(line, MAX_LINE_LEN, fp) != (char *)NULL ) {
  486.         l_num++;
  487.         if (is_comment(line[0]))
  488.             continue;        /* ignore comments */
  489.         npoints++;
  490.         if (i >= samples)    /* should never be > samples */
  491.           continue;        /* Overflow! keep looping to get count */
  492.         if (!line[1]) { /* is it blank line ? */
  493.             /* break in data, make next point undefined */
  494.             this_plot->points[i].type = UNDEFINED;
  495.             i++;
  496.             continue;
  497.         }
  498.  
  499.         switch (sscanf(line, format, &x, &y)) {
  500.             case 1: {        /* only one number on the line */
  501.                y = x;        /* assign that number to y */
  502.                x = datum;    /* and make the index into x */
  503.                /* no break; !!! */
  504.             }
  505.             case 2: {
  506.                if (yfirst) { /* exchange x and y */
  507.                   temp = y;
  508.                   y = x;
  509.                   x = temp;
  510.                }
  511.                datum++;
  512.                this_plot->points[i].type = INRANGE;
  513.  
  514.                if (log_x) {
  515.                   if (x < 0.0) {
  516.                      this_plot->points[i].type = UNDEFINED;
  517.                   } else if (x == 0.0) {
  518.                      this_plot->points[i].type = OUTRANGE;
  519.                      this_plot->points[i].x = -VERYLARGE;
  520.                   } else
  521.                     this_plot->points[i].x = log10(x);
  522.                } else
  523.                 this_plot->points[i].x = x;
  524.  
  525.                if (log_y) {
  526.                   if (y < 0.0) {
  527.                      this_plot->points[i].type = UNDEFINED;
  528.                   } else if (y == 0.0) {
  529.                      this_plot->points[i].type = OUTRANGE;
  530.                      this_plot->points[i].y = -VERYLARGE;
  531.                   } else
  532.                     this_plot->points[i].y = log10(y);
  533.                } else
  534.                 this_plot->points[i].y = y;
  535.  
  536.                if (this_plot->points[i].type == INRANGE)
  537.                 if (   (autoscale_lx || inrange(x,xmin,xmax))
  538.                     && (autoscale_ly || inrange(y,ymin,ymax) || polar)) {
  539.                     if (autoscale_lx) {
  540.                        if (x < xmin) xmin = x;
  541.                        if (x > xmax) xmax = x;
  542.                     }
  543.                     if (autoscale_ly) {
  544.                        if (y < ymin) ymin = y;
  545.                        if (y > ymax) ymax = y;
  546.                     }
  547.                 } else {
  548.                     this_plot->points[i].type = OUTRANGE;
  549.                 }
  550.                i++;
  551.                break;
  552.             }
  553.             default: {
  554.                (void) sprintf(line, "bad data on line %d", l_num);
  555.                int_error(line,c_token);
  556.             }
  557.         }
  558.     }
  559.     if (npoints > samples) {
  560.         (void) sprintf(line,
  561.  "%d data points and breaks found--samples must be set at least this high",
  562.                     npoints); 
  563.         int_error(line,c_token);
  564.     }
  565.     this_plot->p_count = i;
  566.      (void) fclose(fp);
  567. }
  568.  
  569. /* This parses the plot command after any range specifications. 
  570.  * To support autoscaling on the x axis, we want any data files to 
  571.  * define the x range, then to plot any functions using that range. 
  572.  * We thus parse the input twice, once to pick up the data files, 
  573.  * and again to pick up the functions. Definitions are processed 
  574.  * twice, but that won't hurt.
  575.  */
  576. eval_plots()
  577. {
  578. register int i;
  579. register struct curve_points *this_plot, **tp_ptr;
  580. register int start_token, end_token, mysamples;
  581. register int begin_token;
  582. double x_min, x_max, y_min, y_max;
  583. register double x, xdiff, temp;
  584. static struct value a;
  585. BOOLEAN ltmp, some_data_files = FALSE;
  586. int plot_num, line_num, point_num, xparam=0;
  587. char *xtitle;
  588. void parametric_fixup();
  589.  
  590.     mysamples = samples;
  591.  
  592.     if (autoscale_ly) {
  593.         ymin = VERYLARGE;
  594.         ymax = -VERYLARGE;
  595.     } else if (log_y && (ymin <= 0.0 || ymax <= 0.0))
  596.             int_error("y range must be above 0 for log scale!",
  597.                 NO_CARET);
  598.  
  599.     tp_ptr = &(first_plot);
  600.     plot_num = 0;
  601.     line_num = 0;     /* default line type */
  602.     point_num = 0;    /* default point type */
  603.  
  604.      begin_token = c_token;
  605.  
  606. /*** First Pass: Read through data files ***/
  607. /* This pass serves to set the xrange and to parse the command, as well 
  608.  * as filling in every thing except the function data. That is done after
  609.  * the xrange is defined.
  610.  */
  611.     while (TRUE) {
  612.         if (END_OF_COMMAND)
  613.             int_error("function to plot expected",c_token);
  614.  
  615.         start_token = c_token;
  616.  
  617.         if (is_definition(c_token)) {
  618.             define();
  619.         } else {
  620.             plot_num++;
  621.             if (*tp_ptr)
  622.                 this_plot = *tp_ptr;
  623.             else {        /* no memory malloc()'d there yet */
  624.                 this_plot = (struct curve_points *)
  625.                     alloc((unsigned int) (sizeof(struct curve_points) -
  626.                     (MAX_POINTS -(samples+1))*sizeof(struct coordinate)),
  627.                     "curve points");
  628.                 this_plot->next_cp = NULL;
  629.                 this_plot->title = NULL;
  630.                 *tp_ptr = this_plot;
  631.             }
  632.  
  633.             if (isstring(c_token)) {            /* data file to plot */
  634.                 if (parametric && xparam) 
  635.                     int_error("previous parametric function not fully specified",
  636.                                                                     c_token);
  637.  
  638.                 if (!some_data_files && autoscale_lx) {
  639.                     xmin = VERYLARGE;
  640.                     xmax = -VERYLARGE;
  641.                 }
  642.                 some_data_files = TRUE;
  643.  
  644.                 this_plot->plot_type = DATA;
  645.                 this_plot->plot_style = data_style;
  646.                 end_token = c_token;
  647.                 get_data(this_plot); /* this also parses the using option */
  648.             } 
  649.             else {                            /* function to plot */
  650.                 if (parametric)            /* working on x parametric function */
  651.                     xparam = 1 - xparam;
  652.                 this_plot->plot_type = FUNC;
  653.                 this_plot->plot_style = func_style;
  654.                 dummy_func = &plot_func;
  655.                 plot_func.at = temp_at();
  656.                 /* ignore it for now */
  657.                 end_token = c_token-1;
  658.             }
  659.  
  660.             if (almost_equals(c_token,"t$itle")) {
  661.                 if (parametric) {
  662.                     if (xparam) 
  663.                         int_error(
  664.         "\"title\" allowed only after parametric function fully specified",
  665.                                                                     c_token);
  666.                     else if (xtitle != NULL)
  667.                         xtitle[0] = '\0';  /* Remove default title .*/
  668.                 }
  669.                 c_token++;
  670.                 if ( (input_line[token[c_token].start_index]=='\'')
  671.                    ||(input_line[token[c_token].start_index]=='"') ) {
  672.                     m_quote_capture(&(this_plot->title),c_token,c_token);
  673.                 }
  674.                 else {
  675.                     int_error("expecting \"title\" for plot",c_token);
  676.                 }
  677.                 c_token++;
  678.             }
  679.               else {
  680.                   m_capture(&(this_plot->title),start_token,end_token);
  681.                  if (xparam) xtitle = this_plot->title;
  682.               }
  683.   
  684.               this_plot->line_type = line_num;
  685.             this_plot->point_type = point_num;
  686.  
  687.             if (almost_equals(c_token,"w$ith")) {
  688.                 if (parametric && xparam) 
  689.                     int_error("\"with\" allowed only after parametric function fully specified",
  690.                                                                     c_token);
  691.                 this_plot->plot_style = get_style();
  692.             }
  693.  
  694.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  695.                 struct value t;
  696.                 this_plot->line_type = (int)real(const_express(&t))-1;
  697.             }
  698.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  699.                 struct value t;
  700.                 this_plot->point_type = (int)real(const_express(&t))-1;
  701.             }
  702.             if ( (this_plot->plot_style == POINTS) ||
  703.                  (this_plot->plot_style == LINESPOINTS) )
  704.                     if (!xparam) point_num++;
  705.             if (!xparam) line_num++;
  706.  
  707.             tp_ptr = &(this_plot->next_cp);
  708.         }
  709.  
  710.         if (equals(c_token,",")) 
  711.             c_token++;
  712.         else  
  713.             break;
  714.     }
  715.  
  716.     if (parametric && xparam) 
  717.         int_error("parametric function not fully specified", NO_CARET);
  718.  
  719.     if (parametric) {
  720.     /* Swap t and x ranges for duration of these eval_plot computations. */
  721.         ltmp = autoscale_lx; autoscale_lx = autoscale_lt; autoscale_lt = ltmp;
  722.         temp = xmin; xmin = tmin; tmin = temp;
  723.         temp = xmax; xmax = tmax; tmax = temp;
  724.     }
  725.  
  726. /*** Second Pass: Evaluate the functions ***/
  727. /* Everything is defined now, except the function data. We expect
  728.  * no syntax errors, etc, since the above parsed it all. This makes 
  729.  * the code below simpler. If autoscale_ly, the yrange may still change.
  730.  */
  731.      if (xmin == xmax)
  732.       if (autoscale_lx) {
  733.          fprintf(stderr, "Warning: empty %c range [%g:%g], ", 
  734.             parametric ? 't' : 'x', xmin,xmax);
  735.          if (xmin == 0.0) {
  736.             /* completely arbitary */
  737.             xmin = -1.;
  738.             xmax = 1.;
  739.          } else {
  740.             /* expand range by 10% in either direction */
  741.             xmin = xmin * 0.9;
  742.             xmax = xmax * 1.1;
  743.          }
  744.          fprintf(stderr, "adjusting to [%g:%g]\n", xmin,xmax);
  745.       } else {
  746.          int_error("x range is empty", c_token);
  747.       }
  748.  
  749.     /* give error if xrange badly set from missing datafile error */
  750.     if (xmin == VERYLARGE || xmax == -VERYLARGE) {
  751.         int_error("x range is invalid", c_token);
  752.     }
  753.  
  754.     if (log_x) {
  755.        if (xmin <= 0.0 || xmax <= 0.0)
  756.         int_error("x range must be greater than 0 for log scale!",NO_CARET);
  757.        x_min = log10(xmin);
  758.        x_max = log10(xmax);
  759.     } else {
  760.        x_min = xmin;
  761.        x_max = xmax;
  762.     }
  763.     
  764.     xdiff = (x_max - x_min) / mysamples;
  765.     
  766.     plot_num = 0;
  767.     this_plot = first_plot;
  768.     c_token = begin_token;    /* start over */
  769.  
  770.     /* Read through functions */
  771.     while (TRUE) {
  772.         if (is_definition(c_token)) {
  773.             define();
  774.         } else {
  775.             plot_num++;
  776.             if (isstring(c_token)) {            /* data file to plot */
  777.                 /* ignore this now */
  778.                 c_token++;
  779.                 if (almost_equals(c_token,"u$sing")) {
  780.                     c_token++;      /* skip "using" */
  781.                     if (!isstring(c_token))
  782.                         c_token++;    /* skip "xy", "yx" or "y" */
  783.                     c_token++;        /* skip format string */
  784.                 }
  785.             } 
  786.             else {                            /* function to plot */
  787.                 if (parametric)            /* working on x parametric function */
  788.                     xparam = 1 - xparam;
  789.                 dummy_func = &plot_func;
  790.                 plot_func.at = temp_at(); /* reparse function */
  791.     
  792.                 for (i = 0; i <= mysamples; i++) {
  793.                     if (i == samples+1)
  794.                      int_error("number of points exceeded samples",
  795.                              NO_CARET);
  796.                     x = x_min + i*xdiff;
  797.                     /* if (log_x) PEM fix logscale x axis */
  798.                     /* x = pow(10.0,x); 26-Sep-89 */
  799.                     (void) complex(&plot_func.dummy_value,
  800.                                 log_x ? pow(10.0,x) : x,
  801.                                 0.0);
  802.                     
  803.                     evaluate_at(plot_func.at,&a);
  804.                     
  805.                     if (undefined || (fabs(imag(&a)) > zero)) {
  806.                        this_plot->points[i].type = UNDEFINED;
  807.                        continue;
  808.                     }
  809.                     
  810.                     temp = real(&a);
  811.  
  812.                     if (log_y && temp < 0.0) {
  813.                        this_plot->points[i].type = UNDEFINED;
  814.                        continue;
  815.                     }
  816.  
  817.                     this_plot->points[i].x = x;
  818.                     if (log_y) {
  819.                        if (temp == 0.0) {
  820.                           this_plot->points[i].type = OUTRANGE;
  821.                           this_plot->points[i].y = -VERYLARGE;
  822.                           continue;
  823.                        } else {
  824.                           this_plot->points[i].y = log10(temp);
  825.                        }
  826.                     } else
  827.                      this_plot->points[i].y = temp;
  828.  
  829.                     if (autoscale_ly || polar
  830.                        || inrange(temp, ymin, ymax)) {
  831.                        this_plot->points[i].type = INRANGE;
  832.                     /* When xparam is 1 we are not really computing y's! */
  833.                         if (!xparam && autoscale_ly) {
  834.                            if (temp < ymin) ymin = temp;
  835.                            if (temp > ymax) ymax = temp;
  836.                         }
  837.                     } else
  838.                      this_plot->points[i].type = OUTRANGE;
  839.                 }
  840.                 this_plot->p_count = i; /* mysamples + 1 */
  841.              }
  842.             
  843.             /* title was handled above */
  844.             if (almost_equals(c_token,"t$itle")) {
  845.                 c_token++;
  846.                 c_token++;
  847.             }
  848.  
  849.             /* style was handled above */
  850.             if (almost_equals(c_token,"w$ith")) {
  851.                 c_token++;
  852.                 c_token++;
  853.             }
  854.  
  855.             /* line and point types were handled above */
  856.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  857.                 struct value t;
  858.                 (void)real(const_express(&t));
  859.             }
  860.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  861.                 struct value t;
  862.                 (void)real(const_express(&t));
  863.             }
  864.  
  865.             this_plot = this_plot->next_cp;
  866.          }
  867.         
  868.         if (equals(c_token,",")) 
  869.           c_token++;
  870.         else  
  871.           break;
  872.      }
  873.  
  874.     if (ymin == ymax)
  875.      /* if autoscale, widen range */
  876.      if (autoscale_ly) {
  877.         fprintf(stderr, "Warning: empty y range [%g:%g], ", ymin, ymax);
  878.         if (ymin == 0.0) {
  879.             ymin = -1.;
  880.             ymax = 1.;
  881.         } else {
  882.             /* expand range by 10% in either direction */
  883.             ymin = ymin * 0.9;
  884.             ymax = ymax * 1.1;
  885.         }
  886.         fprintf(stderr, "adjusting to [%g:%g]\n", ymin, ymax);
  887.      } else {
  888.         int_error("y range is empty", c_token);
  889.      }
  890.  
  891. /* Now we finally know the real ymin and ymax */
  892.     if (log_y) {
  893.         y_min = log10(ymin);
  894.         y_max = log10(ymax);
  895.     } else {
  896.         y_min = ymin;
  897.         y_max = ymax;
  898.     }
  899.     capture(replot_line,plot_token,c_token);    
  900.  
  901.     if (parametric) {
  902.     /* Now put t and x ranges back before we actually plot anything. */
  903.         ltmp = autoscale_lx; autoscale_lx = autoscale_lt; autoscale_lt = ltmp;
  904.         temp = xmin; xmin = tmin; tmin = temp;
  905.         temp = xmax; xmax = tmax; tmax = temp;
  906.         if (some_data_files && autoscale_lx) {
  907.         /* 
  908.             Stop any further autoscaling in this case (may be a mistake, have
  909.               to consider what is really wanted some day in the future--jdc). 
  910.         */
  911.             autoscale_lx = 0;
  912.         }
  913.     /* Now actually fix the plot pairs to be single plots. */
  914.         parametric_fixup (first_plot, &plot_num, &x_min, &x_max);
  915.     }
  916.     do_plot(first_plot,plot_num,x_min,x_max,y_min,y_max);
  917. }
  918.  
  919.  
  920.  
  921. done(status)
  922. int status;
  923. {
  924.     if (term && term_init)
  925.         (*term_tbl[term].reset)();
  926. #ifdef VMS
  927.     vms_reset();
  928. #endif
  929.     exit(status);
  930. }
  931.  
  932. void parametric_fixup (start_plot, plot_num, x_min, x_max)
  933. struct curve_points *start_plot;
  934. int *plot_num;
  935. double *x_min, *x_max;
  936. /*
  937.     The hardest part of this routine is collapsing the FUNC plot types
  938.    in the list (which are gauranteed to occur in (x,y) pairs while 
  939.     preserving the non-FUNC type plots intact.  This means we have to
  940.     work our way through various lists.  Examples (hand checked):
  941.         start_plot:F1->F2->NULL ==> F2->NULL
  942.         start_plot:F1->F2->F3->F4->F5->F6->NULL ==> F2->F4->F6->NULL
  943.         start_plot:F1->F2->D1->D2->F3->F4->D3->NULL ==> F2->D1->D2->F4->D3->NULL
  944.     
  945.     Of course, the more interesting work is to move the y values of
  946.     the x function to become the x values of the y function (checking
  947.     the mins and maxs as we go along).
  948. */
  949. {
  950.     struct curve_points *xp, *new_list, *yp = start_plot, *tmp, 
  951.             *free_list, *free_head=NULL;
  952.     int i, tlen, curve; 
  953.     char *new_title;
  954.     double lxmin, lxmax, temp;
  955.  
  956.     if (autoscale_lx) {
  957.         lxmin = VERYLARGE;
  958.         lxmax = -VERYLARGE;
  959.     } else {
  960.         lxmin = xmin;
  961.         lxmax = xmax;
  962.     }
  963.  
  964. /* 
  965.     Ok, go through all the plots and move FUNC types together.  Note: this
  966.     originally was written to look for a NULL next pointer, but gnuplot 
  967.     wants to be sticky in grabbing memory and the right number of items
  968.     in the plot list is controlled by the plot_num variable.
  969.  
  970.     Since gnuplot wants to do this sticky business, a free_list of 
  971.     curve_points is kept and then tagged onto the end of the plot list as
  972.     this seems more in the spirit of the original memory behavior than
  973.     simply freeing the memory.  I'm personally not convinced this sort
  974.     of concern is worth it since the time spent computing points seems
  975.     to dominate any garbage collecting that might be saved here...
  976. */
  977.     new_list = xp = start_plot; 
  978.     yp = xp->next_cp;
  979.    curve = 0;
  980.     for (; curve < *plot_num; xp = xp->next_cp,yp = yp->next_cp,curve++) {
  981.         if (xp->plot_type != FUNC) {
  982.             continue;
  983.         }
  984.     /* Here's a FUNC parametric function defined as two parts. */
  985.         --(*plot_num);
  986.     /* 
  987.         Go through all the points assigning the y's from xp to be the
  988.         x's for yp.  Check max's and min's as you go.
  989.     */
  990.         for (i = 0; i < yp->p_count; ++i) {
  991.         /* 
  992.             Throw away excess xp points, mark excess yp points as OUTRANGE.
  993.         */
  994.             if (i > xp->p_count) {
  995.                 yp->points[i].type == OUTRANGE;
  996.                 continue;
  997.             }
  998.         /* 
  999.             Just as we had to do when we computed y values--now check that
  1000.             x's (computed parametrically) are in the permitted ranges as well.
  1001.         */
  1002.             temp = xp->points[i].y;   /* New x value for yp function. */
  1003.             yp->points[i].x = temp;
  1004.         /* For legitimate y values, let the x values decide if they plot. */
  1005.             if (yp->points[i].type == INRANGE)
  1006.                 yp->points[i].type = xp->points[i].type;  
  1007.             if (autoscale_lx || polar
  1008.                        || inrange(temp, lxmin, lxmax)) {
  1009.                if (autoscale_lx && temp < lxmin) lxmin = temp;
  1010.                 if (autoscale_lx && temp > lxmax) lxmax = temp;
  1011.             } else
  1012.             yp->points[i].type = OUTRANGE;  /* Due to x value. */
  1013.         }
  1014.    /* Ok, fix up the title to include both the xp and yp plots. */
  1015.         if (xp->title && xp->title[0] != '\0') {
  1016.             tlen = strlen (yp->title) + strlen (xp->title) + 3;
  1017.           new_title = alloc ((unsigned int) tlen, "string");
  1018.             strcpy (new_title, xp->title);  
  1019.             strcat (new_title, ", ");       /* + 2 */
  1020.             strcat (new_title, yp->title);  /* + 1 = + 3 */
  1021.             free (yp->title);
  1022.             yp->title = new_title;
  1023.         }
  1024.     /* Eliminate the first curve (xparam) and just use the second. */
  1025.         if (xp == start_plot) {
  1026.         /* Simply nip off the first element of the list. */
  1027.             new_list = first_plot = yp;
  1028.             xp = xp->next_cp;
  1029.             if (yp->next_cp != NULL)
  1030.                 yp = yp->next_cp;
  1031.         /* Add start_plot to the free_list. */
  1032.             if (free_head == NULL) {
  1033.                 free_list = free_head = start_plot;
  1034.                 free_head->next_cp = NULL;
  1035.             } else {
  1036.                 free_list->next_cp = start_plot;
  1037.                 start_plot->next_cp = NULL;
  1038.                 free_list = start_plot;
  1039.             }
  1040.         }
  1041.         else {
  1042.         /* Here, remove the xp node and replace it with the yp node. */
  1043.            tmp = xp;
  1044.         /* Pass over any data files that might have been in place. */
  1045.             while (new_list->next_cp && new_list->next_cp != xp) 
  1046.                 new_list = new_list->next_cp;
  1047.             new_list->next_cp = yp;
  1048.             new_list = new_list->next_cp;
  1049.             xp = xp->next_cp;
  1050.             if (yp->next_cp != NULL)
  1051.                 yp = yp->next_cp;
  1052.         /* Add tmp to the free_list. */
  1053.             if (free_head == NULL) {
  1054.                 free_list = free_head = tmp;
  1055.             } else {
  1056.                 free_list->next_cp = tmp;
  1057.                 tmp->next_cp = NULL;
  1058.                 free_list = tmp;
  1059.             }
  1060.         }
  1061.     }
  1062. /* Ok, stick the free list at the end of the curve_points plot list. */
  1063.     while (new_list->next_cp != NULL)
  1064.         new_list = new_list->next_cp;
  1065.     new_list->next_cp = free_head;
  1066.  
  1067. /* Report the overall graph mins and maxs. */
  1068.     *x_min = lxmin;
  1069.     *x_max = lxmax;
  1070. }
  1071.  
  1072.  
  1073. #ifdef MSDOS
  1074. #ifndef __TURBOC__    /* Turbo C already has sleep() */
  1075. #ifndef __ZTC__     /* ZTC already has usleep() */
  1076. /* kludge to provide sleep() for msc 5.1 */
  1077. void sleep(delay)
  1078. unsigned int delay;
  1079. {
  1080. unsigned long time_is_up;
  1081.     time_is_up = time(NULL) + (unsigned long) delay; 
  1082.     while (time(NULL)<time_is_up)
  1083.         /* wait */ ;
  1084. }
  1085. #endif /* not ZTC */
  1086. #endif /* not TURBOC */
  1087. #endif /* MSDOS */
  1088.  
  1089.  
  1090. /* Support for input, shell, and help for various systems */
  1091.  
  1092. #ifdef vms
  1093.  
  1094. #include <descrip.h>
  1095. #include <rmsdef.h>
  1096. #include <errno.h>
  1097.  
  1098. extern lib$get_input(), lib$put_output();
  1099.  
  1100. int vms_len;
  1101.  
  1102. unsigned int status[2] = {1, 0};
  1103.  
  1104. static char help[MAX_LINE_LEN+1] = "gnuplot";
  1105.  
  1106. $DESCRIPTOR(prompt_desc,PROMPT);
  1107. $DESCRIPTOR(line_desc,input_line);
  1108.  
  1109. $DESCRIPTOR(help_desc,help);
  1110. $DESCRIPTOR(helpfile_desc,"GNUPLOT$HELP");
  1111.  
  1112.  
  1113. read_line(prompt)
  1114. char *prompt;
  1115. {
  1116.     int more, start=0;
  1117.     char expand_prompt[40];
  1118.  
  1119.     prompt_desc.dsc$w_length = strlen (prompt);
  1120.     prompt_desc.dsc$a_pointer = prompt;
  1121.     (void) strcpy (expand_prompt, "_");
  1122.     (void) strncat (expand_prompt, prompt, 38);
  1123.     do {
  1124.         line_desc.dsc$w_length = MAX_LINE_LEN - start;
  1125.         line_desc.dsc$a_pointer = &input_line[start];
  1126.         switch(status[1] = lib$get_input(&line_desc, &prompt_desc, &vms_len)){
  1127.           case RMS$_EOF:
  1128.           done(IO_SUCCESS);    /* ^Z isn't really an error */
  1129.           break;
  1130.           case RMS$_TNS:    /* didn't press return in time *
  1131.                            /
  1132.                            vms_len--; /* skip the last character */
  1133.           break;            /* and parse anyway */
  1134.           case RMS$_BES:    /* Bad Escape Sequence */
  1135.           case RMS$_PES:    /* Partial Escape Sequence */
  1136.           sys$putmsg(status);
  1137.           vms_len = 0;        /* ignore the line */
  1138.           break;
  1139.           case SS$_NORMAL:
  1140.           break;            /* everything's fine */
  1141.           default:
  1142.           done(status[1]);    /* give the error message */
  1143.         }
  1144.         start += vms_len;
  1145.         input_line[start] = '\0';
  1146.        inline_num++;
  1147.         if (input_line[start-1] == '\\') {
  1148.           /* Allow for a continuation line. */
  1149.           prompt_desc.dsc$w_length = strlen (expand_prompt);
  1150.           prompt_desc.dsc$a_pointer = expand_prompt;
  1151.           more = 1;
  1152.           --start;
  1153.         }
  1154.         else {
  1155.           line_desc.dsc$w_length = strlen(input_line);
  1156.           line_desc.dsc$a_pointer = input_line;
  1157.           more = 0;
  1158.         }
  1159.     } while (more);
  1160. }
  1161.  
  1162.  
  1163. do_help()
  1164. {
  1165.     help_desc.dsc$w_length = strlen(help);
  1166.     if ((vaxc$errno = lbr$output_help(lib$put_output,0,&help_desc,
  1167.         &helpfile_desc,0,lib$get_input)) != SS$_NORMAL)
  1168.             os_error("can't open GNUPLOT$HELP");
  1169. }
  1170.  
  1171.  
  1172. do_shell()
  1173. {
  1174.     if ((vaxc$errno = lib$spawn()) != SS$_NORMAL) {
  1175.         os_error("spawn error",NO_CARET);
  1176.     }
  1177. }
  1178.  
  1179.  
  1180. do_system()
  1181. {
  1182.     input_line[0] = ' ';    /* an embarrassment, but... */
  1183.  
  1184.     if ((vaxc$errno = lib$spawn(&line_desc)) != SS$_NORMAL)
  1185.         os_error("spawn error",NO_CARET);
  1186.  
  1187.     (void) putc('\n',stderr);
  1188. }
  1189.  
  1190. #else /* vms */
  1191.  
  1192. /* do_help: (not VMS, although it would work)
  1193.  * Give help to the user. 
  1194.  * It parses the command line into helpbuf and supplies help for that 
  1195.  * string. Then, if there are subtopics available for that key,
  1196.  * it prompts the user with this string. If more input is
  1197.  * given, do_help is called recursively, with the argument the index of 
  1198.  * null character in the string. Thus a more specific help can be 
  1199.  * supplied. This can be done repeatedly. 
  1200.  * If null input is given, the function returns, effecting a
  1201.  * backward climb up the tree.
  1202.  * David Kotz (dfk@cs.duke.edu) 10/89
  1203.  */
  1204. do_help()
  1205. {
  1206.     static char helpbuf[MAX_LINE_LEN] = "";
  1207.     static char prompt[MAX_LINE_LEN] = "";
  1208.     int base;                /* index of first char AFTER help string */
  1209.     int len;                /* length of current help string */
  1210.     BOOLEAN more_help;
  1211.     BOOLEAN only;            /* TRUE if only printing subtopics */
  1212.     int subtopics;            /* 0 if no subtopics for this topic */
  1213.     int start;                /* starting token of help string */
  1214.     char *help_ptr;            /* name of help file */
  1215.  
  1216.     if ( (help_ptr = getenv("GNUHELP")) == (char *)NULL )
  1217.         /* if can't find environment variable then just use HELPFILE */
  1218.         help_ptr = HELPFILE;
  1219.  
  1220.     len = base = strlen(helpbuf);
  1221.  
  1222.     /* find the end of the help command */
  1223.     for (start = c_token; !(END_OF_COMMAND); c_token++)
  1224.      ;
  1225.     /* copy new help input into helpbuf */
  1226.     if (len > 0)
  1227.      helpbuf[len++] = ' ';    /* add a space */
  1228.     capture(helpbuf+len, start, c_token-1);
  1229.     squash_spaces(helpbuf+base); /* only bother with new stuff */
  1230.     lower_case(helpbuf+base); /* only bother with new stuff */
  1231.     len = strlen(helpbuf);
  1232.  
  1233.     /* now, a lone ? will print subtopics only */
  1234.     if (strcmp(helpbuf + (base ? base+1 : 0), "?") == 0) {
  1235.        /* subtopics only */
  1236.        subtopics = 1;
  1237.        only = TRUE;
  1238.        helpbuf[base] = '\0';    /* cut off question mark */
  1239.     } else {
  1240.        /* normal help request */
  1241.        subtopics = 0;
  1242.        only = FALSE;
  1243.     }
  1244.  
  1245.     switch (help(helpbuf, help_ptr, &subtopics)) {
  1246.        case H_FOUND: {
  1247.           /* already printed the help info */
  1248.           /* subtopics now is true if there were any subtopics */
  1249.           screen_ok = FALSE;
  1250.     
  1251.           do {
  1252.              if (subtopics && !only) {
  1253.                 /* prompt for subtopic with current help string */
  1254.                 if (len > 0)
  1255.                   (void) sprintf(prompt, "Subtopic of %s: ", helpbuf);
  1256.                 else
  1257.                   (void) strcpy(prompt, "Help topic: ");
  1258.                 read_line(prompt);
  1259.                 num_tokens = scanner(input_line);
  1260.                 c_token = 0;
  1261.                 more_help = !(END_OF_COMMAND);
  1262.                 if (more_help)
  1263.                   /* base for next level is all of current helpbuf */
  1264.                   do_help();
  1265.              } else 
  1266.                more_help = FALSE;
  1267.           } while(more_help);
  1268.     
  1269.           break;
  1270.        }
  1271.        case H_NOTFOUND: {
  1272.           printf("Sorry, no help for '%s'\n", helpbuf);
  1273.           break;
  1274.        }
  1275.        case H_ERROR: {
  1276.           perror(help_ptr);
  1277.           break;
  1278.        }
  1279.        default: {        /* defensive programming */
  1280.           int_error("Impossible case in switch\n", NO_CARET);
  1281.           /* NOTREACHED */
  1282.        }
  1283.     }
  1284.     
  1285.     helpbuf[base] = '\0';    /* cut it off where we started */
  1286. }
  1287.  
  1288. do_system()
  1289. {
  1290.     if (system(input_line + 1))
  1291.         os_error("system() failed",NO_CARET);
  1292. }
  1293.  
  1294. #ifdef MSDOS
  1295.  
  1296. read_line(prompt)
  1297.     char *prompt;
  1298. {
  1299.     register int i;
  1300.     int start = 0, ilen = 0;
  1301.     BOOLEAN more;
  1302.     int last;
  1303.     
  1304. #ifndef __ZTC__
  1305.     if (interactive) { /* if interactive use console IO so CED will work */
  1306.         cputs(prompt);
  1307.         do {
  1308.            ilen = MAX_LINE_LEN-start-1;
  1309.            input_line[start] = ilen > 126 ? 126 : ilen;
  1310.            (void) cgets(&(input_line[start]));
  1311.            (void) putc('\n',stderr);
  1312.            if (input_line[start+2] == 26) {
  1313.               /* end-of-file */
  1314.               (void) putc('\n',stderr);
  1315.               input_line[start] = '\0';
  1316.               inline_num++;
  1317.               if (start > 0)    /* don't quit yet - process what we have */
  1318.                 more = FALSE;
  1319.               else {
  1320.                  (void) putc('\n',stderr);
  1321.                  done(IO_SUCCESS);
  1322.                  /* NOTREACHED */
  1323.               }
  1324.            } else {
  1325.               /* normal line input */
  1326.               register i = start;
  1327.               while ( (input_line[i] = input_line[i+2]) != (char)NULL )
  1328.                 i++;        /* yuck!  move everything down two characters */
  1329.  
  1330.               inline_num++;
  1331.               last = strlen(input_line) - 1;
  1332.               if (last + 1 >= MAX_LINE_LEN)
  1333.                 int_error("Input line too long",NO_CARET);
  1334.                      
  1335.               if (input_line[last] == '\\') { /* line continuation */
  1336.                  start = last;
  1337.                  more = TRUE;
  1338.               } else
  1339.                 more = FALSE;
  1340.            }
  1341.            if (more)
  1342.             cputs("> ");
  1343.         } while(more);
  1344.     }
  1345.     else { /* not interactive */
  1346. #endif /* not ZTC */
  1347.         if (interactive)
  1348.          fputs(prompt,stderr);
  1349.         do {
  1350.            /* grab some input */
  1351.            if ( fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin) 
  1352.                     == (char *)NULL ) {
  1353.               /* end-of-file */
  1354.               if (interactive)
  1355.                 (void) putc('\n',stderr);
  1356.               input_line[start] = '\0';
  1357.               inline_num++;
  1358.               if (start > 0)    /* don't quit yet - process what we have */
  1359.                 more = FALSE;
  1360.               else
  1361.                 done(IO_SUCCESS); /* no return */
  1362.            } else {
  1363.               /* normal line input */
  1364.               last = strlen(input_line) - 1;
  1365.               if (input_line[last] == '\n') { /* remove any newline */
  1366.                  input_line[last] = '\0';
  1367.                  /* Watch out that we don't backup beyond 0 (1-1-1) */
  1368.                  if (last > 0) --last;
  1369.                  inline_num++;
  1370.               } else if (last+1 >= MAX_LINE_LEN)
  1371.                 int_error("Input line too long",NO_CARET);
  1372.                      
  1373.               if (input_line[last] == '\\') { /* line continuation */
  1374.                  start = last;
  1375.                  more = TRUE;
  1376.               } else
  1377.                 more = FALSE;
  1378.            }
  1379.             if (more && interactive)
  1380.             fputs("> ", stderr);
  1381.         } while(more);
  1382. #ifndef __ZTC
  1383.     }
  1384. #endif
  1385. }
  1386.  
  1387.  
  1388. do_shell()
  1389. {
  1390. register char *comspec;
  1391.     if ((comspec = getenv("COMSPEC")) == (char *)NULL)
  1392.         comspec = "\command.com";
  1393.     if (spawnl(P_WAIT,comspec,NULL) == -1)
  1394.         os_error("unable to spawn shell",NO_CARET);
  1395. }
  1396.  
  1397. #else /* MSDOS */
  1398.         /* plain old Unix */
  1399.  
  1400. read_line(prompt)
  1401.     char *prompt;
  1402. {
  1403.     int start = 0;
  1404.     BOOLEAN more;
  1405.     int last = 0;
  1406.  
  1407.     if (interactive)
  1408.      fputs(prompt,stderr);
  1409.     do {
  1410.        /* grab some input */
  1411.        if ( fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin) 
  1412.                 == (char *)NULL ) {
  1413.           /* end-of-file */
  1414.           if (interactive)
  1415.             (void) putc('\n',stderr);
  1416.           input_line[start] = '\0';
  1417.           inline_num++;
  1418.           if (start > 0)    /* don't quit yet - process what we have */
  1419.             more = FALSE;
  1420.           else
  1421.             done(IO_SUCCESS); /* no return */
  1422.        } else {
  1423.           /* normal line input */
  1424.           last = strlen(input_line) - 1;
  1425.           if (input_line[last] == '\n') { /* remove any newline */
  1426.              input_line[last] = '\0';
  1427.                 /* Watch out that we don't backup beyond 0 (1-1-1) */
  1428.              if (last > 0) --last;
  1429.              inline_num++;
  1430.           } else if (last+1 >= MAX_LINE_LEN)
  1431.             int_error("Input line too long",NO_CARET);
  1432.                  
  1433.           if (input_line[last] == '\\') { /* line continuation */
  1434.              start = last;
  1435.              more = TRUE;
  1436.           } else
  1437.             more = FALSE;
  1438.        }
  1439.         if (more && interactive)
  1440.         fputs("> ", stderr);
  1441.     } while(more);
  1442. }
  1443.  
  1444. #ifdef VFORK
  1445.  
  1446. do_shell()
  1447. {
  1448. register char *shell;
  1449. register int p;
  1450. static int execstat;
  1451.     if (!(shell = getenv("SHELL")))
  1452.         shell = SHELL;
  1453.     if ((p = vfork()) == 0) {
  1454.         execstat = execl(shell,shell,NULL);
  1455.         _exit(1);
  1456.     } else if (p == -1)
  1457.         os_error("vfork failed",c_token);
  1458.     else
  1459.         while (wait(NULL) != p)
  1460.             ;
  1461.     if (execstat == -1)
  1462.         os_error("shell exec failed",c_token);
  1463.     (void) putc('\n',stderr);
  1464. }
  1465. #else /* VFORK */
  1466.  
  1467. #define EXEC "exec "
  1468. do_shell()
  1469. {
  1470. static char exec[100] = EXEC;
  1471. register char *shell;
  1472.     if (!(shell = getenv("SHELL")))
  1473.         shell = SHELL;
  1474.  
  1475.     if (system(strncpy(&exec[sizeof(EXEC)-1],shell,
  1476.         sizeof(exec)-sizeof(EXEC)-1)))
  1477.         os_error("system() failed",NO_CARET);
  1478.  
  1479.     (void) putc('\n',stderr);
  1480. }
  1481. #endif /* VFORK */
  1482. #endif /* MSDOS */
  1483. #endif /* vms */
  1484.