/*  utils.c  28 April 1999  */

/* copyright 1987, 1988, 1989 Phil Andrews, Pittsburgh Supercomputing Center */
/* all rights reserved */
/* utility functions */

# include <stdio.h>
# include <stdlib.h>
# include <math.h>
# include <ctype.h>
# include <string.h>

# include "defs.h"
/* some include files vary in location */
#ifdef VAXC
#include time
static tbuffer_t time_b;
#else
#include <sys/types.h>
#include <sys/times.h>
#ifdef CRAY1
#include </usr/include/sys/machd.h>
#endif
#ifdef CRAY1
#include </usr/include/sys/param.h>
#endif
static struct tms time_b;
#endif

/* 
  Some systems don't define HZ 
*/
#ifndef HZ
#define HZ 60
#endif

/* memory allocation */
void *malloc(), *realloc();

/* more globals for time info*/
static int elaps_time;  /* elapsed time in seconds */
static float proc_used;  /* the process time used so far in seconds */
static long etime;
/* need this for VMS */
#define toint(inchar) ((int) inchar - (int) '0')

#define float_size (sizeof(float))

/* more globals */
static struct one_opt *new_opt = NULL;  /* put command line options here */

/*
  PROCEDURE DECLARATIONS
*/

int get_angle ( int *ptr, float *deg, double *theta );

/* and now the actual utility routines */



/* starts the clock running */
initialise_time()
{
  (void) times(&time_b);  /* void for compatibility */
#ifdef VAXC
  proc_used = time_b.proc_user_time * 0.01;
#else
  proc_used = (float) (time_b.tms_utime + time_b.tms_stime) / HZ;
#endif
  etime = time(NULL);
  return(1);
}
/* says how much we used */
write_time(pages_done)
     int pages_done;
{
  char buffer[80];
  
  times(&time_b);
#ifdef VAXC
  proc_used = time_b.proc_user_time * 0.01 - proc_used;
#else
  proc_used = ((float) (time_b.tms_utime + time_b.tms_stime) / HZ)
    - proc_used;
#endif
  elaps_time = time(NULL) - etime;
  fprintf(stderr, "\nCPU time: %.2f sec., elapsed time: %d sec.",
    proc_used, elaps_time);
#ifndef VAXC
  fprintf(stderr, "\n");
#endif
  /* and put it in the log file (if it exists) */
  sprintf(buffer, "%.2f %d %d", proc_used, elaps_time, pages_done);
  log_line(buffer, 0, NULL, 1, 0, 3);
  return(1);
}

/* take care of necessary memory, note must clear by hand for compatibility */
unsigned char *allocate_mem(size, clear)
     int size, clear;
{
  unsigned char *mem_ptr;
  int i;
  
  if ( !(mem_ptr = (unsigned char *) malloc(size)) ) {
    fprintf(stderr, "\ncouldn't allocate (%d bytes) memory !\n", size);
    return(NULL);
  } else if (clear) for (i=0; i<size; ++i) *(mem_ptr + i) = 0;
  
  return(mem_ptr);
}
/* utility, given a string decides whether page_no is desired */
/* help file entry follows: */
/*
  GTEX FOO/PAGES="-1,2,5,7_9,-10_-5" or
  GTEX FOO/PAGES=(-1,2,5,7_9,-10_-5)
  allows the selective printing of individual or sets of pages. 
  The above page string means do pages -1, 2, 5, 7 thru 9 and -10 thru -5.
  */
want_page(page_no, page_string)
     int page_no;
     char page_string[];
{
  char *sptr, cd, *strpbrk();
  int length, n1, n2;
  
  if (page_string[0] == '*') return(1);   /* default */
  length = strlen(page_string);
  
  /* read first number */
  if (0==sscanf(page_string, "%d", &n1)) return(0);
  if (page_no == n1) return(1);
  sptr = page_string;
  while (sptr = strpbrk(sptr, ",_") )
    switch (*sptr) {
    case ',': ++sptr; if (0==sscanf(sptr, "%d", &n1)) return(0);
      if (n1==page_no) return(1); break;
      
    case '_': ++sptr; if (0==sscanf(sptr, "%d", &n2)) return(0);
      if ( (n1 <= page_no) && (page_no <= n2)) return(1);
      break;
    default:  return(0); }
  
  return(0);  /* no go */
}
/* now one of the few routine to contain both VMS and UNIX stuff */
/* unfortunately pretty messy, since I try to use the same code for the
   very different VMS and UNIX command syntaxes via macros */
#ifdef VAXC
#include descrip
#endif
/* parse the command line here */
parse_cline(cmd_str, argc, argv, to_screen, do_list, out_info, opt)
     int argc, *to_screen, *do_list;
     char **argv, *cmd_str;
     struct info_struct *out_info;
     struct one_opt *opt;  /* put command line options here */
{
  float my_float();
  int my_int(), i;
  char *my_string(), *my_list();
  /* macros and device dependencies */
  
#define init_opt(name_val, name_str, in_char, type, init)\
  strncpy(opt[(int) name_val].flag_str,name_str, flags_l);\
    opt[(int) name_val].flag_char = in_char;\
      opt[(int) name_val].set = 0; switch (type){\
      case onoff: case integer:\
  sscanf(init, "%d", &opt[(int) name_val].val.i); break;\
  case real:\
  sscanf(init, "%f", &opt[(int) name_val].val.r); break;\
  case lst: case str:\
    strncpy(opt[(int) name_val].val.str, init,  max_str);}; \
    opt[(int) name_val].tag = type;
  
  /* now the tricky stuff */
  
#ifdef VMS  /* VMS specifics */
#define check_opt(name_val, flag) \
  if (my_present(flag) & 1) {\
    opt[(int) name_val].set = 1; \
      switch (opt[(int) name_val].tag)\
  {\
   case onoff: opt[(int) name_val].val.i = 1; break;\
   case integer: opt[(int) name_val].val.i = my_int(flag); break;\
   case real: opt[(int) name_val].val.r = my_float(flag); break;\
   case str: strcpy(opt[(int) name_val].val.str, my_string(flag)); break;\
   case lst: strcpy(opt[(int) name_val].val.str, my_list(flag)); break;}}
#define init_check(name_val, name_str, in_char, type, init)\
  init_opt(name_val, name_str, in_char, type, init) \
    check_opt(name_val, name_str)
    
#else  /* UNIX stuff */
#define check_opt(name_val, flag) \
    if (isdigit(flag)) \
      { if ((endc + toint(flag)) < argc)\
    {strncpy(opt[(int) name_val].val.str, argv[endc + toint(flag)], max_str);\
       opt[(int) name_val].set = 1;}}\
    else \
      {\
   for (i=endc;(i>0) && !((argv[i][0] == '-') && (argv[i][1]==flag));--i);\
     if (i) { opt[(int) name_val].set = 1; switch(opt[(int) name_val].tag)\
          {\
           case onoff: opt[(int) name_val].val.i = 1; break;\
           case integer:   sscanf(&argv[i][2], "%d", &opt[(int) name_val].val.i); break;\
           case real:   sscanf(&argv[i][2], "%f", &opt[(int) name_val].val.r); break;\
           case str: case lst:\
       strncpy(opt[(int) name_val].val.str, &argv[i][2], max_str); break;}}}
#define init_check(name_val, name_str, in_char, type, init)            \
  init_opt(name_val, name_str, in_char, type, init) check_opt(name_val, in_char)
#endif
    
    static int called_before = 0;  /* has this routine been called before ? */
  /* VMS specifics */
#ifdef VMS  /* first setup the parsing */
  int GPT_COMMANDS( );  /* THE COMMAND TABLE */  
  int CLI$DCL_PARSE( );     /* parse the command */
  int LIB$GET_INPUT( );    /* get input from the tty */
  static $DESCRIPTOR(prompt, "GPT> ");  /* our prompt */
  int status;
  char option_str[max_str], *my_string();
  struct dsc$descriptor_s str_desc;
  extern void init_defs();  /* in cgm.c */
  extern int ask_device();  /* cgm.c */
  option_str[0] = '\0';
  str_desc.dsc$w_length = strlen(cmd_str);
  str_desc.dsc$a_pointer = cmd_str;
  str_desc.dsc$b_class  = DSC$K_CLASS_S;
  str_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  
  status = CLI$DCL_PARSE(&str_desc, &GPT_COMMANDS, &LIB$GET_INPUT,
       &LIB$GET_INPUT, &prompt);  /* get the command */
#else
  int endc;
  if (argc <= 0) {
    fprintf(stderr, "usage gplot/gtex [options] input_name output_name\n");
    return(0);
  }
  /* figure out if we have any file names (i.e. non - options) */
  *cmd_str = '\0';
  for (i=0; i<argc; i++) sprintf (&cmd_str[strlen(cmd_str)], "%s ", argv[i]);
  for (i=argc-1;(i>0) && (argv[i][0] != '-'); --i); 
  /* are we using stdin for input ? */
  if ((argv[i][0] == '-') && (argv[i][1] == '\0')) {
    --i;
    strcpy(opt[(int) in_name].val.str, "-");  /* make it explicit */
    opt[(int) in_name].set = 1;
  }
  endc = i;
#endif
  /* actually do it, format is (enum_name, vms_name, unix_char, type, default) */  
  
  if (!called_before)  {  /* first time */
    init_check(device, "device", 'd', str, "nodev");
    init_check(diaquest, "diaquest", 'v', str, "CSA0:");
#ifdef vms
    init_check(lvr,"lvr",'V',str,"TTA0:");
#else
    init_check(lvr,"lvr",'V',str,"/dev/tty0");
#endif
    init_check(eject, "eject", 'E', onoff, "0");
    init_check(copies, "copies", 'c', integer, "1");
    init_check(debug, "debug", 'D', onoff, "0");
    init_check(list, "list", 'l', onoff, "0");
    init_check(pxl_in, "pxl_in", 'P', real, "0.0");
    init_check(ypxl_in, "ypxl_in", 'Q',  real, "0.0");
    init_check(screen, "screen", 's', str, "nodev");
    init_check(start, "start", 'b', integer, "0");
    init_check(tty, "tty", 't',  onoff, "0");
    init_check(out_name, "out_name", '2', str, "foobar");
    init_check(user, "username", 'u', str, "unknown");
  }
  /* now the stuff that can be reset */
  init_check(x_offset, "x_offset", 'x',  real, "0.0");
  init_check(y_offset, "y_offset", 'y',  real, "0.0");
  init_check(x_size, "x_size", 'X', real, "0.0");
  init_check(y_size, "y_size", 'Y', real, "0.0");
  init_check(included, "included", 'i',  onoff, "0");
  init_check(index_file, "index_file", 'I', onoff, "0");
  init_check(degrees, "degrees", 'r', real, "0.0");
  init_check(pages, "pages", 'p', lst, "*");
  init_check(title_string, "title_string", 'T',  str, "none");
  init_check(clear_text, "clear_text", 'C', onoff, "0");
  init_check(in_name, "in_name", '1', str, "foo");
  init_check(text_mag, "text_mag", 'm',  real, "1.0");
  init_check(font_type, "font_type", 'f', integer, "0");
  init_check(scale_output, "scale_output", 'S',  integer, "0");
  init_check(nindex, "nindex", 'N', onoff, "0");
  
  ++called_before;
  
  /* if we're debugging, tell what options are set */
  
  if (opt[(int) debug].set) for (i=0; i <= (int) out_name; ++i) {
    if (opt[i].set) {
      fprintf(stderr, "%s: ", opt[i].flag_str);
      switch (opt[i].tag) {
      case onoff: fprintf(stderr, "%s\n", (opt[i].val.i) ? "on" : "off");
  break;
      case integer: fprintf(stderr, "value = %d\n", opt[i].val.i);break;
      case real: fprintf(stderr, "value = %f\n", opt[i].val.r);break;
      case str: fprintf(stderr, "value = %s\n", opt[i].val.str);break;
      case lst: fprintf(stderr, "value = %s\n", opt[i].val.str);break;
      }
    }
  }
  /* set the other arguments */
  *to_screen = opt[(int) screen].set;
  *do_list = opt[(int) list].set;
  
  /* now send off info to the logging line routine */
  log_line(cmd_str, argc, argv, 0, 1, 1);
  
  return(1);
}
/* device characteristics may be overridden by the command line */
consult_device(opt, out_info)
     struct info_struct *out_info;
     struct one_opt *opt;  /* put command line options here */
/*
#define may_override(name) if (opt[(int) name].set) switch (opt[(int) name].tag) {      \
                          case integer: out_info->name = opt[(int) name].val.i; break;      \
                          case real: out_info->name = opt[(int) name].val.r; break;      \
                          case lst: case str: strcpy(out_info->name, opt[(int) name].val.str); break;}
*/
{
  /* and now overide the device if the command line wants us to */
/*
  may_override(pxl_in);
  may_override(ypxl_in);
  may_override(x_offset);
  may_override(y_offset);
  may_override(x_size);
  may_override(y_size);
*/

#ifdef VMS
  if (opt[(int) screen].set) {
    strcpy(opt[(int) out_name].val.str, "SYS$OUTPUT:");
    opt[(int) out_name].set = 1;
  }
#endif
  /* store pointer to option */
  new_opt = opt;
  
  return(1);
}
/* function to set cgm status structures to their startup default values */
/* note that we do not do this at compile time because of the possibility of
   processing multiple cgm files. */

void s_defaults(c1, c2, c3, c5, g5, pxlvdc_in)
     struct   mf_d_struct   *c1;  /* the class 1 elements */
     struct   pic_d_struct   *c2;  /* the class 2 elements */
     struct   control_struct  *c3;  /* the class 3 elements */
     struct   attrib_struct  *c5, *g5;  /* the class 5 elements */
     double pxlvdc_in;      /* the pxl_vdc value */
{
  int i, nsteps;
  /* and set up the initial colour table values */
#define init_csize 13    /* how many entries */
  static float init_ctab[3 * init_csize]  /* global memory */
    = {         /* my defaults */
      1.0,   1.0,   1.0,    /* white */
      0.0,   0.0,  0.0,    /* black */
      1.0,   0.0,  0.0,    /* red */
      0.0,  1.0,  0.0,    /* green */
      0.0,  0.0,   1.0,    /* blue */
      1.0,   1.0,  0.0,    /* yellow */
      1.0,  0.0,   1.0,    /* magenta */
      0.0,  1.0,  1.0,    /* cyan */
      1.0,   0.5,  0.0,    /* orange */
      1.0,   0.0,  0.5,    /* purple-red */
      0.5,   0.0,  1.0,    /* blue-purple */
      0.5,   1.0,  0.0,    /* yellow-green */
      0.0,   1.0,  0.5    /* bluish-green */
      };
  
  /* first the class1, metafile descriptor elements */
  c1->vdc_type     = vdc_int;
  c1->int_prec    = 16;
  c1->real_prec.fixed   = 1;
  c1->real_prec.exp  = 16;
  c1->real_prec.fract  = 16;
  c1->ind_prec    = 16;
  c1->col_prec    = 8;
  c1->col_i_prec    = 8;
  c1->max_c_index    = 63;
  for (i=0; i<3; ++i) {
    c1->c_v_extent.min[i] = 0;
    c1->c_v_extent.max[i] = 255;
  }
  c1->char_c_an    = 0;
  
  /* now class2, the  picture descriptor elements */
  c2->scale_mode.s_mode  = 0;
  c2->scale_mode.m_scaling= 0.0;
  c2->c_s_mode    = i_c_mode;
  c2->l_w_s_mode    = scaled;
  c2->m_s_s_mode    = scaled;
  c2->e_w_s_mode    = scaled;
  for (i=0; i<2; ++i) {
    c2->vdc_extent.i[i]  = 0;
    c2->vdc_extent.r[i]  = 0.0;
    c2->vdc_extent.i[i + 2]  = 32767;
    c2->vdc_extent.r[i + 2]  = 1.0;
  }
  c2->back_col.red  = 1.0;
  c2->back_col.green  = 1.0;
  c2->back_col.blue  = 1.0;
  /* now the control elements, class 3 */
  /* note that everything except vdc_extent is kept in pixel units */
  c3->vdc_i_prec    = 16;
  c3->vdc_r_prec.fixed  = 1;
  c3->vdc_r_prec.exp  = 16;
  c3->vdc_r_prec.fract  = 16;
  c3->aux_col.red    = 1.0;
  c3->aux_col.green  = 1.0;
  c3->aux_col.blue  = 1.0;
  c3->aux_col.ind    = 0;
  c3->transparency  = on;
  for (i=0; i<4; ++i) {
    c3->clip_rect.i[i]  = pxlvdc_in * c2->vdc_extent.i[i];
    c3->clip_rect.r[i]  = pxlvdc_in * c2->vdc_extent.r[i];
  }
  c3->clip_ind    = on;
  /* class 5, the attribute elements */
  c5->l_b_index    = 1;
  c5->line_type    = solid_l;
  c5->line_width.i  = pxlvdc_in * 33;
  c5->line_width.r  = 1.0;
  c5->line_colour.red  = 0.0;
  c5->line_colour.green  = 0.0;
  c5->line_colour.blue  = 0.0;
  c5->line_colour.ind  = 1;
  c5->mk_b_index    = 1;
  c5->mk_type    = 1;
  c5->mk_size.i    = pxlvdc_in * 328;/* standard has error  */
  c5->mk_size.r    = 1.0;
  c5->mk_colour.red  = 0.0;
  c5->mk_colour.green  = 0.0;
  c5->mk_colour.blue  = 0.0;
  c5->mk_colour.ind  = 1;
  c5->t_b_index    = 1;
  c5->t_f_index    = 1;
  c5->t_prec    = string;
  c5->c_exp_fac    = 1.0;
  c5->c_space    = 0.0;
  c5->text_colour.red  = 0.0;
  c5->text_colour.green  = 0.0;
  c5->text_colour.blue  = 0.0;
  c5->text_colour.ind  = 1;
  c5->c_height    = pxlvdc_in * 328;
  if (new_opt && (new_opt[(int) text_mag].set))
    c5->c_height *= new_opt[(int) text_mag].val.r;
  c5->c_orient.x_up  = 0;
  c5->c_orient.y_up  = 1;
  c5->c_orient.x_base  = 1;
  c5->c_orient.y_base  = 0;
  c5->text_path    = right;
  c5->text_align.hor  = normal_h;
  c5->text_align.ver  = normal_v;
  c5->text_align.cont_hor  = 0.0;
  c5->text_align.cont_ver  = 0.0;
  c5->c_set_index    = 1;
  c5->a_c_set_index  = 1;
  c5->f_b_index    = 1;
  c5->int_style    = hollow;
  c5->fill_colour.red  = 0.0;
  c5->fill_colour.green  = 0.0;
  c5->fill_colour.blue  = 0.0;
  c5->fill_colour.ind  = 1;
  c5->hatch_index    = 1;
  c5->pat_index    = 1;
  c5->e_b_index    = 1;
  c5->edge_type    = solid_l;
  c5->edge_width.i  = pxlvdc_in * 33;
  c5->edge_width.r  = 1.0;
  c5->edge_colour.red  = 0.0;
  c5->edge_colour.green  = 0.0;
  c5->edge_colour.blue  = 0.0;
  c5->edge_colour.ind  = 1;
  c5->edge_vis    = off;
  for (i=0; i<2; ++i) {
    c5->fill_ref.i[i]  = 0;
    c5->fill_ref.r[i]  = 0.0;
  }
  for (i=0; i<4; ++i) {
    c5->pat_size.i[i]  = pxlvdc_in * c2->vdc_extent.i[i];
    c5->pat_size.r[i]  = pxlvdc_in * c2->vdc_extent.r[i];
  }
  /* now allocate both colour tables */
  c5->ctab  = 
    (float *) allocate_mem((c1->max_c_index + 1) * 3 * float_size, 1);
  g5->ctab  = 
    (float *) allocate_mem((c1->max_c_index + 1) * 3 * float_size, 1);
  
  /* and fill them in with the defaults */
  
  for (i = 0; (i< (3 * init_csize)) && (i< 3*(c1->max_c_index + 1)); ++i) 
    *(g5->ctab + i) = *(c5->ctab + i) = init_ctab[i];
  nsteps = (c1->max_c_index + 1 - init_csize) / 3;
  if (nsteps <= 0) {
    (void) fprintf(stderr, "illegal nsteps %d\n", nsteps);
    return;
  }
  for (i = 3 * init_csize; i < 3 * (init_csize + nsteps); i+=3) {
    *(g5->ctab + i) = *(c5->ctab + i) = 
      (float) (i - 3 * init_csize)/ (3 * nsteps);  
    *(g5->ctab + i + 1) = *(c5->ctab + i + 1) = 0.0;
    *(g5->ctab + i + 2) = *(c5->ctab + i + 2) = 0.0;
  }
  for (i = 3 * (init_csize + nsteps); i < 3 * (init_csize + 2 * nsteps);
       i+=3) {
    *(g5->ctab + i) = *(c5->ctab + i) = 0.0;
    *(g5->ctab + i + 1) = *(c5->ctab + i + 1) = 
      (float) (i - 3 * (init_csize + nsteps))/ (3 * nsteps);  
    *(g5->ctab + i + 2) = *(c5->ctab + i + 2) = 0.0;
  }
  for (i = 3 * (init_csize + 2 * nsteps); i <= 3 * c1->max_c_index; 
       i+=3) {
    *(g5->ctab + i) = *(c5->ctab + i) = 0.0;
    *(g5->ctab + i + 1) = *(c5->ctab + i + 1) = 0.0;
    *(g5->ctab + i + 2) = *(c5->ctab + i + 2) =
      (float) (i - 3 * (init_csize + 2 * nsteps)) / 
  (3 * (c1->max_c_index - (init_csize + 2 * nsteps)));
  }
  /* pattern, bundle tables later */
  
}
/* function to reset cgm status structures to the correct default values
   (preumably at the beginning of a new page). The cn variables are the
   ones to be set, the dn are defaults that may have been changed by a
   metafile defaults replacement element. */

void rs_defaults(c1, c2, c3, c5, d2, d3, d5, pxlvdc_in)
     struct   mf_d_struct   *c1;  /* the class 1 elements */
     struct   pic_d_struct   *c2, *d2;  /* the class 2 elements */
     struct   control_struct  *c3, *d3;  /* the class 3 elements */
     struct   attrib_struct  *c5, *d5;  /* the class 5 elements */
     double pxlvdc_in;      /* the pxl_vdc value */
{
  int i;
  double vdc_long, vdc_try;
  
  /* first class2, the  picture descriptor elements */
  c2->scale_mode.s_mode  = d2->scale_mode.s_mode;
  c2->scale_mode.m_scaling= d2->scale_mode.m_scaling;
  c2->c_s_mode    = d2->c_s_mode;
  c2->l_w_s_mode    = d2->l_w_s_mode;
  c2->m_s_s_mode    = d2->m_s_s_mode;
  c2->e_w_s_mode    = d2->e_w_s_mode;
  for (i=0; i<2; ++i) {
    c2->vdc_extent.i[i]  = d2->vdc_extent.i[i];
    c2->vdc_extent.r[i]  = d2->vdc_extent.r[i];
    c2->vdc_extent.i[i + 2]  = d2->vdc_extent.i[i + 2];
    c2->vdc_extent.r[i + 2]  = d2->vdc_extent.r[i + 2];
  }
  c2->back_col.red  = d2->back_col.red;
  c2->back_col.green  = d2->back_col.green;
  c2->back_col.blue  = d2->back_col.blue;
  /* now the control elements, class 3 */
  c3->vdc_i_prec    = d3->vdc_i_prec;
  c3->vdc_r_prec.fixed  = d3->vdc_r_prec.fixed;
  c3->vdc_r_prec.exp  = d3->vdc_r_prec.exp;
  c3->vdc_r_prec.fract  = d3->vdc_r_prec.fract;
  c3->aux_col.red    = d3->aux_col.red;
  c3->aux_col.green  = d3->aux_col.green;
  c3->aux_col.blue  = d3->aux_col.blue;
  c3->aux_col.ind    = d3->aux_col.ind;
  c3->transparency  = d3->transparency;
  for (i=0; i<4; ++i) {
    c3->clip_rect.i[i]  = d3->clip_rect.i[i];
    c3->clip_rect.r[i]  = d3->clip_rect.r[i];
  }
  c3->clip_ind    = d3->clip_ind;
  /* class 5, the attribute elements */
  /* these include some that depend on earlier defaults, so first: */
  switch (c1->vdc_type) {
  case vdc_int:  
    vdc_long = abs(d2->vdc_extent.i[2] - d2->vdc_extent.i[0]);
    vdc_try  = abs(d2->vdc_extent.i[3] - d2->vdc_extent.i[1]);
    break;
  case vdc_real:  
    vdc_long = fabs(d2->vdc_extent.r[2] - d2->vdc_extent.r[0]);
    vdc_try  = fabs(d2->vdc_extent.r[3] - d2->vdc_extent.r[1]);
    break;
  }
  vdc_long = (vdc_long >= vdc_try) ? vdc_long : vdc_try; /* longest side */
  
  /* now make the setting */
  c5->l_b_index    = d5->l_b_index;
  c5->line_type    = d5->line_type;
  c5->line_width.i  = pxlvdc_in * vdc_long / 1000 + 0.5;
  c5->line_width.r  = d5->line_width.r;
  c5->line_colour.red  = d5->line_colour.red;
  c5->line_colour.green  = d5->line_colour.green;
  c5->line_colour.blue  = d5->line_colour.blue;
  c5->line_colour.ind  = d5->line_colour.ind;
  c5->mk_b_index    = d5->mk_b_index;
  c5->mk_type    = d5->mk_type;
  c5->mk_size.i    = pxlvdc_in * vdc_long / 100 + 0.5;
  c5->mk_size.r    = d5->mk_size.r;
  c5->mk_colour.red  = d5->mk_colour.red;
  c5->mk_colour.green  = d5->mk_colour.green;
  c5->mk_colour.blue  = d5->mk_colour.blue;
  c5->mk_colour.ind  = d5->mk_colour.ind;
  c5->t_b_index    = d5->t_b_index;
  c5->t_f_index    = d5->t_f_index;
  c5->t_prec    = d5->t_prec;
  c5->c_exp_fac    = d5->c_exp_fac;
  c5->c_space    = d5->c_space;
  c5->text_colour.red  = d5->text_colour.red;
  c5->text_colour.green  = d5->text_colour.green;
  c5->text_colour.blue  = d5->text_colour.blue;
  c5->text_colour.ind  = d5->text_colour.ind;
  c5->c_height    = pxlvdc_in * vdc_long / 100 + 0.5;
  if (new_opt && (new_opt[(int) text_mag].set))
    c5->c_height *= new_opt[(int) text_mag].val.r;
  c5->c_orient.x_up  = d5->c_orient.x_up;
  c5->c_orient.y_up  = d5->c_orient.y_up;
  c5->c_orient.x_base  = d5->c_orient.x_base;
  c5->c_orient.y_base  = d5->c_orient.y_base;
  c5->text_path    = d5->text_path;
  c5->text_align.hor  = d5->text_align.hor;
  c5->text_align.ver  = d5->text_align.ver;
  c5->text_align.cont_hor  = d5->text_align.cont_hor;
  c5->text_align.cont_ver  = d5->text_align.cont_ver;
  c5->c_set_index    = d5->c_set_index;
  c5->a_c_set_index  = d5->a_c_set_index;
  c5->f_b_index    = d5->f_b_index;
  c5->int_style    = d5->int_style;
  c5->fill_colour.red  = d5->fill_colour.red;
  c5->fill_colour.green  = d5->fill_colour.green;
  c5->fill_colour.blue  = d5->fill_colour.blue;
  c5->fill_colour.ind  = d5->fill_colour.ind;
  c5->hatch_index    = d5->hatch_index;
  c5->pat_index    = d5->pat_index;
  c5->e_b_index    = d5->e_b_index;
  c5->edge_type    = d5->edge_type;
  c5->edge_width.i  = pxlvdc_in * vdc_long / 1000 + 0.5;
  c5->edge_width.r  = d5->edge_width.r;
  c5->edge_colour.red  = d5->edge_colour.red;
  c5->edge_colour.green  = d5->edge_colour.green;
  c5->edge_colour.blue  = d5->edge_colour.blue;
  c5->edge_colour.ind  = d5->edge_colour.ind;
  c5->edge_vis    = d5->edge_vis;
  for (i=0; i<2; ++i) {
    c5->fill_ref.i[i]  = d5->fill_ref.i[i];
    c5->fill_ref.r[i]  = d5->fill_ref.r[i];
  }
  for (i=0; i<4; ++i) {
    c5->pat_size.i[i]  = d5->pat_size.i[i];
    c5->pat_size.r[i]  = d5->pat_size.r[i];
  }
  for (i = 0; i< 3 * (c1->max_c_index + 1); ++i) 
    *(c5->ctab + i) = *(d5->ctab + i);
  /* tables later */
  
}
/* begin VMS only stuff here, this could all be removed under UNIX */
/* use a VAXC flag since may use these on VMS but with VMS undefined */
#ifdef VAXC
#include rms
#include ssdef    /* systems services commands */
#include climsgdef  /* CLI interface */
/* this from the old trn.h file */
#define LNM$_STRING 2
#define LNM$M_CASE_BLIND 33554432
static int lnm_attr = LNM$M_CASE_BLIND;

/* this is from the old jbc.h file */
#define SJC$_ADD_FILE 2
#define SJC$_CLOSE_JOB 9
#define SJC$_CREATE_JOB 10
#define SJC$_DELETE_FILE 24
#define SJC$_FILE_SPECIFICATION 42
#define SJC$_JOB_NAME 79
#define SJC$_JOB_STATUS_OUTPUT 88
#define SJC$_NOTIFY 108
#define SJC$_QUEUE 134
/* more globals */
static char global_string[max_str];
#define max_no_items 5
#define first_flag 1
#define second_flag 14
struct one_item {  unsigned short buffer_length;
      unsigned short item_code;
      char *buffer_address;
      int *return_length_address;  };

struct my_struct {  unsigned short s_length;  /* ASCIID */
      char flag2;
      char flag1;
      char *s_ptr; };


static struct one_item item_array[max_no_items];
static struct my_struct t_name, l_name;
/* checks to see if the argument is present on the command line */
my_present(in_string)
     char *in_string;
{
  int ret = 0;
  /* in VMS use the CLI interface provided */
  int CLI$PRESENT( ), LIB$SIG_TO_RET();  
  struct dsc$descriptor_s name_desc;
  
  name_desc.dsc$w_length   = strlen(in_string);
  name_desc.dsc$a_pointer  = in_string;
  name_desc.dsc$b_class  = DSC$K_CLASS_S;
  name_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  /* don't bother with error trapping, presume right .cld file linked */
  ret = CLI$PRESENT(&name_desc);
  return(ret); 
}
char *my_string(in_string)
     char *in_string;
{
#define c_length 128
  int CLI$GET_VALUE( ); 
  char out_string[c_length];
  struct dsc$descriptor_s name_desc, out_desc;
  short str_l;
  int ret, ret_value, i;
  
  for (i=0; i<c_length; ++i) out_string[i] = ' ';
  name_desc.dsc$w_length   = strlen(in_string);
  name_desc.dsc$a_pointer  = in_string;
  name_desc.dsc$b_class  = DSC$K_CLASS_S;
  name_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  out_desc.dsc$w_length   = c_length;
  out_desc.dsc$a_pointer  = out_string;
  out_desc.dsc$b_class  = DSC$K_CLASS_S;
  out_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  
  ret = CLI$GET_VALUE(&name_desc, &out_desc, &str_l);
  out_string[str_l] = '\0';
  strcpy(global_string, out_string);
  return(global_string);
#undef c_length
}
int my_int(in_string)
     char *in_string;
{
  char *my_string();
  int ret_value;
  sscanf(my_string(in_string), "%d", &ret_value);
  return(ret_value);
}
float my_float(in_string)
     char *in_string;
{
  char *my_string();
  float ret_value;
  sscanf(my_string(in_string), "%f", &ret_value);
  return(ret_value);
}
/* utility to grab a list from the command line */
char *my_list(in_string)
     char *in_string;
{
#define c_length 20
  int CLI$GET_VALUE( ); 
  char out_string[c_length];
  struct dsc$descriptor_s name_desc, out_desc;
  short str_l;
  int ret, ret_value, i;
  
  for (i=0; i<c_length; ++i) out_string[i] = ' ';
  name_desc.dsc$w_length   = strlen(in_string);
  name_desc.dsc$a_pointer  = in_string;
  name_desc.dsc$b_class  = DSC$K_CLASS_S;
  name_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  out_desc.dsc$w_length   = c_length;
  out_desc.dsc$a_pointer  = out_string;
  out_desc.dsc$b_class  = DSC$K_CLASS_S;
  out_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  
  global_string[0] = '\0';
  while ( (ret = CLI$GET_VALUE(&name_desc, &out_desc, &str_l)) &1){
    out_string[str_l] = '\0';
    if (ret == CLI$_COMMA) strcat(out_string, ",");
    if (ret == CLI$_CONCAT) strcat(out_string, "+");
    strcat(global_string, out_string);
  }
  if (ret != CLI$_ABSENT) LIB$SIGNAL(ret);
  return(global_string);
#undef c_length
}

/* routine to get help for the user  */
get_help(which_help, help_lib)
     char *which_help, *help_lib;
{
  long width = 80, flags;
  int LIB$PUT_OUTPUT(), LIB$OUTPUT_HELP(), LIB$GET_INPUT();
  char init_help[128+1];
  struct dsc$descriptor_s help_desc, lib_desc;
#define HLP$M_PROMPT  1
#define HLP$M_PROCESS  2
#define HLP$M_GROUP  4
#define HLP$M_SYSTEM  8
  
  strcpy(init_help, which_help);
  
  help_desc.dsc$w_length   = strlen(init_help);
  help_desc.dsc$a_pointer  = init_help;
  help_desc.dsc$b_class  = DSC$K_CLASS_S;
  help_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  lib_desc.dsc$w_length   = strlen(help_lib);
  lib_desc.dsc$a_pointer  = help_lib;
  lib_desc.dsc$b_class  = DSC$K_CLASS_S;
  lib_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  flags = HLP$M_PROMPT;
  
  LBR$OUTPUT_HELP(LIB$PUT_OUTPUT, &width, &help_desc, &lib_desc,
      &flags, LIB$GET_INPUT);
  return(1);
}
/* procedure to get integer values from a list from a cli qualifier */
get_list(out_ptr, in_string, no_items)
     char *in_string;
     int *out_ptr, no_items;
{
#define c_length 20
  int CLI$GET_VALUE( ); 
  char out_string[c_length];
  struct dsc$descriptor_s name_desc, out_desc;
  short str_l;
  int ret, ret_value, i;
  
  for (i=0; i<c_length; ++i) out_string[i] = ' ';
  name_desc.dsc$w_length   = strlen(in_string);
  name_desc.dsc$a_pointer  = in_string;
  name_desc.dsc$b_class  = DSC$K_CLASS_S;
  name_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  out_desc.dsc$w_length   = c_length;
  out_desc.dsc$a_pointer  = out_string;
  out_desc.dsc$b_class  = DSC$K_CLASS_S;
  out_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  
  i = 0;
  while (( (ret = CLI$GET_VALUE(&name_desc, &out_desc, &str_l)) &1)
   && (i < no_items) ) {
    out_string[str_l] = '\0';
    sscanf(out_string, "%d", out_ptr + i);
    ++i;
  }
  if ( (ret != CLI$_ABSENT) && (i < no_items) ) {
    LIB$SIGNAL(ret);
    return(0);
  }
  return(1);
}
/* get the command line here using a vax rtl call */
get_cmd_line(outstr, str_len, argc, argv)
     char *outstr, *argv[];
     int str_len, argc;
{
  int i, cind = 0;
  short out_len = 0;
  static char prompt[] = "Command Line ? ";
  struct dsc$descriptor_s str_desc, prmptdesc;
  int ret, LIB$GET_FOREIGN(), fp = 0;
  
  str_desc.dsc$w_length = str_len;
  str_desc.dsc$a_pointer = outstr;
  str_desc.dsc$b_class  = DSC$K_CLASS_S;
  str_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  prmptdesc.dsc$w_length = strlen(prompt);
  prmptdesc.dsc$a_pointer = prompt;
  prmptdesc.dsc$b_class  = DSC$K_CLASS_S;
  prmptdesc.dsc$b_dtype  = DSC$K_DTYPE_T;
  
  if (SS$_NORMAL != (ret = LIB$GET_FOREIGN(&str_desc, &prmptdesc, &out_len, &fp)))
    LIB$SIGNAL(ret);
  
  *(outstr + out_len) = 0;
  return(out_len);
  
}
/* utility to translate a VMS logical */
translate(log_name, tab_name, trans_name)
     char *log_name,   /* the logical to be translated */
       *tab_name,    /* the logical name table to use */
       *trans_name;    /* where to put the trans. (better be big enough !)*/
{
  int result, item_no = 0, n_chars = 0;
  l_name.s_length = strlen(log_name);
  l_name.flag2 = second_flag;
  l_name.flag1 = first_flag;
  l_name.s_ptr = log_name;
  
  t_name.s_length = strlen(tab_name);
  t_name.flag2 = second_flag;
  t_name.flag1 = first_flag;
  t_name.s_ptr = tab_name;
  
  item_array[item_no].buffer_length = max_str;
  item_array[item_no].item_code = LNM$_STRING;
  item_array[item_no].buffer_address = trans_name;
  item_array[item_no].return_length_address = &n_chars;
  
  ++item_no;
  item_array[item_no].item_code = 0;
  item_array[item_no].buffer_length = 0;
  
  
  result = sys$trnlnm(&lnm_attr,&t_name, &l_name, 0,item_array); 
  if (n_chars >= max_str){
    fprintf(stderr, "\nlogical string too long, %d > %d",
      n_chars, max_str);
    n_chars = max_str - 1;
  }
  
  trans_name[n_chars] = '\0';
  if (result != SS$_NORMAL) 
    return(0);
  else return(result);
}
/* utility to create a VMS logical */
make_lnm(log_name, tab_name, trans_name)
     char *log_name,   /* the logical to be translated */
       *tab_name,    /* the logical name table to use */
       *trans_name;    /* what name to translate to */
{
  int result, item_no = 0, n_chars = 0, acmode = 0, attr = 0;
  l_name.s_length = strlen(log_name);
  l_name.flag2 = second_flag;
  l_name.flag1 = first_flag;
  l_name.s_ptr = log_name;
  
  t_name.s_length = strlen(tab_name);
  t_name.flag2 = second_flag;
  t_name.flag1 = first_flag;
  t_name.s_ptr = tab_name;
  
  item_array[item_no].buffer_length = strlen(trans_name);
  item_array[item_no].item_code = LNM$_STRING;
  item_array[item_no].buffer_address = trans_name;
  item_array[item_no].return_length_address = &n_chars;
  
  ++item_no;
  item_array[item_no].item_code = 0;
  item_array[item_no].buffer_length = 0;
  
  result = sys$crelnm(&attr,&t_name, &l_name, 0,item_array); 
  
  if (result != SS$_NORMAL) {
    if (new_opt && (new_opt[(int) debug].set)) LIB$SIGNAL(result);
    else return(0);
  } else return(result);
}

/* create a job in the specified VMS queue */
static int delete_files;
create_job(queue_name, job_name, notify, do_delete)
     char *queue_name, *job_name;
     int notify, do_delete;
{
  int item_no = 0, result;
  
  delete_files = do_delete;
  item_array[item_no].item_code = SJC$_QUEUE;
  item_array[item_no].buffer_length = strlen(queue_name);
  item_array[item_no].buffer_address = queue_name;
  item_array[item_no].return_length_address = 0;
  
  ++item_no;
  item_array[item_no].item_code = SJC$_JOB_NAME;
  item_array[item_no].buffer_length = strlen(job_name);
  item_array[item_no].buffer_address = job_name;
  item_array[item_no].return_length_address = 0;
  
  if (notify) {
    ++item_no;
    item_array[item_no].item_code = SJC$_NOTIFY;
    item_array[item_no].buffer_length = 0;
    item_array[item_no].buffer_address = 0;
    item_array[item_no].return_length_address = 0;
  }
  ++item_no;
  item_array[item_no].item_code = 0;
  
  result = sys$sndjbc(0,SJC$_CREATE_JOB,0,item_array,0,0,0);
  if (result != SS$_NORMAL) {
    fprintf(stderr, "\nproblem with create_job for queue %s,SNDJBC = %d",
      queue_name, result);
    LIB$SIGNAL(result);
  }
  
  return(result);
}

/* adds a file to the VMS queue */
add_file(file_name, deletable)
     char *file_name;
     int deletable;
{
  int item_no = 0, result;
  
  item_array[item_no].item_code = SJC$_FILE_SPECIFICATION;
  item_array[item_no].buffer_length = strlen(file_name);
  item_array[item_no].buffer_address = file_name;
  item_array[item_no].return_length_address = 0;
  
  if ( (delete_files) && (deletable) ) {
    ++item_no;
    item_array[item_no].item_code = SJC$_DELETE_FILE;
    item_array[item_no].buffer_length = 0;
    item_array[item_no].buffer_address = 0;
    item_array[item_no].return_length_address = 0;
  }
  
  ++item_no;
  item_array[item_no].item_code = 0;
  
  result = sys$sndjbc(0,SJC$_ADD_FILE,0,item_array,0,0,0);
  if (result != SS$_NORMAL) 
    fprintf(stderr, "\nproblem with adding %s, SNDJBC = %d",file_name,
      result);
  
  return(result);
}

/* closes a VMS queue job */
/* NOTE!!! we are asking for a status return to be written into job_msg
   this can be very DANGEROUS!!! there appears to be a time lag between return
   from the sndjbc call and the actual writing, if job_msg was a local variable
   it might not get written into until the subroutine had returned, with
   highly unpredictable and dangerous results. Thus we must make it a global 
   variable, wait for a while and check for success. Note that I wait for
   a maximum of 5 seconds, rather than calling $SYNCH, which might wait for
   ever, or might not work, just as sys$sndjbcw doesn't work */
close_job(queue_name, job_msg, msg_length, n_chars, out_name)
     char *queue_name, *job_msg, out_name;
     int msg_length, *n_chars;
{
  int result, item_no = 0, i;
  *n_chars = 0;
  item_array[item_no].buffer_length = msg_length;
  item_array[item_no].item_code = SJC$_JOB_STATUS_OUTPUT;
  item_array[item_no].buffer_address = job_msg;
  item_array[item_no].return_length_address = n_chars;
  
  ++item_no;
  item_array[item_no].item_code = 0;
  
  result = sys$sndjbc(0,SJC$_CLOSE_JOB,0,item_array,0,0,0);
  if (result != SS$_NORMAL) LIB$SIGNAL(result);
  
  for (i=0; (i<5) && (*n_chars==0); ++i) sleep(1);
  
  if ( (*n_chars > 0) && (*n_chars <= msg_length) ) {
    job_msg[*n_chars] = '\0';
    fprintf(stderr, "\n%s", job_msg);
  }
  else fprintf(stderr, "\ncouldn't communicate with %s, output in %s", 
         queue_name, out_name);
  
  return(result);
}

/* look for a default name for GTEX to start up with */
try_def_name(file_name)
     char *file_name;
{
  char input_name[max_str];
  if (translate("TEX$OUTPUT", "LNM$FILE_DEV", input_name) & 1)
    strcpy(file_name, input_name);
  return(1);
}
make_name(new_bits, def_bits, resultant, size, out_file)
     char *new_bits, *def_bits, *resultant;
     int size, out_file;
     /* if out_file, the file to be opened is an output one, don't want old area */
{
  struct FAB mfb;
  struct NAM mnb;
#define b_size 128
  char str_buffer[b_size + 1];
  int rms_status;
  extern int SYS$PARSE();
  
  mfb = cc$rms_fab;
  mnb = cc$rms_nam;
  
  mfb.fab$l_dna = def_bits;
  mfb.fab$b_dns = strlen(def_bits);
  
  mfb.fab$l_fna = new_bits;
  mfb.fab$b_fns = strlen(new_bits);
  
  mfb.fab$l_nam = &mnb;  /* name block */
  
  mnb.nam$b_nop = NAM$M_SYNCHK;
  
  mnb.nam$l_esa = str_buffer;
  mnb.nam$b_ess = b_size;
  
  rms_status = SYS$PARSE(&mfb);
  
  if (out_file) {
    strncpy(resultant, mnb.nam$l_name, (int) mnb.nam$b_name
      + (int) mnb.nam$b_type);
    resultant[mnb.nam$b_name + mnb.nam$b_type] = '\0';
  } else {
    strncpy(resultant, str_buffer, (int) mnb.nam$b_esl);
    resultant[mnb.nam$b_esl] = '\0';
  }
  
  
  return(rms_status);
}

#endif  /* this should come at the very end of the file */

/* utility routine to see if a file exists */
find_file(fname)
     char *fname;
{
#ifdef VAXC
  int LIB$FIND_FILE(), LIB$FIND_FILE_END(), i, ret, user_flags, context,
  stv_addr, LIB$SIGNAL(), j, rb;
  struct dsc$descriptor_s out_desc, rel_desc, name_desc;
  char out_name[max_str], rel_name[max_str];
  
  name_desc.dsc$a_pointer  = fname;
  name_desc.dsc$w_length   = strlen(fname);
  name_desc.dsc$b_class  = DSC$K_CLASS_S;
  name_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  out_desc.dsc$a_pointer  = out_name;
  out_desc.dsc$w_length   = (sizeof out_name) - 1;
  out_desc.dsc$b_class  = DSC$K_CLASS_S;
  out_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  rel_desc.dsc$a_pointer  = rel_name;
  rel_desc.dsc$w_length   = 0;
  rel_desc.dsc$b_class  = DSC$K_CLASS_S;
  rel_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  user_flags = 0;    /* allow wild cards and memory */
  context = 0;
  
  ret = LIB$FIND_FILE(&name_desc, &out_desc,
          &context, 0, &rel_desc, &stv_addr, &user_flags);
  
  if ( (ret == RMS$_NMF) && (ret == RMS$_NORMAL) ) 
    LIB$FIND_FILE_END(&context);  /* recover memory */
  
  return(ret == RMS$_NORMAL);
  
#endif
  return (1);
} 
/* utility routine to find all possible magnifications for pk files */
find_mags(poss_mags, max_mags, exp_str, fname)
     int *poss_mags, max_mags;
     char *exp_str, *fname;
{
  int no_mags, i;
  char dir_str[max_str];
#ifdef VAXC
  int LIB$FIND_FILE(), LIB$FIND_FILE_END(), ret, user_flags, context,
  stv_addr, LIB$SIGNAL(), j, rb;
  struct dsc$descriptor_s out_desc, rel_desc, name_desc;
  char out_name[max_str], rel_name[max_str];
  
  /* create the name we want to look for */
  sprintf(dir_str, "%s*]%s.pk;", exp_str, fname);
  name_desc.dsc$a_pointer  = dir_str;
  name_desc.dsc$w_length   = strlen(dir_str);
  name_desc.dsc$b_class  = DSC$K_CLASS_S;
  name_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  out_desc.dsc$a_pointer  = out_name;
  out_desc.dsc$w_length   = (sizeof out_name) - 1;
  out_desc.dsc$b_class  = DSC$K_CLASS_S;
  out_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  rel_desc.dsc$a_pointer  = rel_name;
  rel_desc.dsc$w_length   = 0;
  rel_desc.dsc$b_class  = DSC$K_CLASS_S;
  rel_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  user_flags = 0;    /* allow wild cards and memory */
  context = 0;
  
  no_mags = 0;
  while ((RMS$_NORMAL == ( ret = LIB$FIND_FILE(&name_desc, &out_desc,
                 &context, 0, &rel_desc, &stv_addr, &user_flags))) 
   && (no_mags<max_mags)) {
    out_name[out_desc.dsc$w_length] = 0;
    /* now grab the directory spec */
    for (j=out_desc.dsc$w_length-1; (j>0) && (out_name[j]!=']'); --j);
    rb = j = j - 1;
    for (;(j>0) && (out_name[j] >= '0') && (out_name[j] <= '9'); --j);
    if ((j > 0) && (j < rb)) {
      sscanf(out_name + j + 1, "%d", poss_mags + no_mags);
      ++no_mags;
    }
  }
  if ( (ret != RMS$_NMF) && (ret != RMS$_NORMAL) ) {
    return(0);
  }
  LIB$FIND_FILE_END(&context);  /* recover memory */
  /* now take care of the UNIX stuff */ 
#else
#define b_size 256
  FILE *popen(), *p_ptr;
  char c, in_buffer[b_size + 1], *b_ptr;
  
  /* create the name we want to look for */
  sprintf(dir_str, "ls %s/*/%s.pk", exp_str, fname);
  p_ptr = popen(dir_str, "r");
  /* now look for possible files */
  no_mags = 0;
  if (p_ptr) {
    while (fgets(in_buffer, b_size, p_ptr) && (no_mags<max_mags)) {
      b_ptr = in_buffer + strlen(in_buffer);
      /* now go back past one / */
      while ((b_ptr >= in_buffer) && (*b_ptr != '/')) --b_ptr;
      /* now another */
      --b_ptr;
      while ((b_ptr >= in_buffer) && (*b_ptr != '/')) --b_ptr;
      ++b_ptr; /* forward one char */
      if (sscanf(b_ptr, "%d", poss_mags + no_mags)) ++no_mags;
      else fprintf(stderr, "couldn't parse %s\n", in_buffer);
    }
    pclose(p_ptr);
  } else {  /* try this */
    *poss_mags = 1000;
    no_mags = 1;
  }
#undef b_size
#endif
  poss_mags[no_mags] = 0;
  
  if (new_opt && (new_opt[(int) debug].set)) {
    fprintf(stderr, "%s poss mags", fname);
    for (i=0; (i < no_mags) && poss_mags[i]; ++i) 
      fprintf(stderr, ", %d", poss_mags[i]);
    fputc('\n', stderr);
  }
  return(no_mags);
}
/* function to return index of closest match to argument in array */
/* assume 0 is not a possibility */
find_best(want, have, no_have)
     int want, no_have, have[];
{
  int i, mag_dif, best_so_far;
  
  mag_dif = 0;
  for (i=0; i<no_have; ++i) if (mag_dif<2 * abs(have[i]))
    mag_dif = 2 * abs(have[i]);
  
  if(mag_dif < 2 * want) mag_dif = 2 * want;
  
  best_so_far = 0;
  
  for (i=0; i<no_have; ++i) 
    if ( (have[i] != 0) && (abs(want - have[i]) < mag_dif) )
      mag_dif = abs(want - have[best_so_far = i]);
  
  if (new_opt && (new_opt[(int) debug].set))
    fprintf(stderr, "wanted %d, use %d\n", want, have[best_so_far]);
  
  return(best_so_far);
}
/* takes a string and returns corresponding argc and argv */
fake_call(instr, argc1, argv1)
     char *instr;
     int *argc1;
     char ***argv1;
{
  char *my_ptr;
#define b_size 256
  static char buffer[b_size + 1];
#define p_size 56
  static char *ptrs[p_size];
  int tokens_found;
  char *b_ptr = buffer;
  
  my_ptr = instr;
  tokens_found = 0;
  *argv1 = ptrs;
  while ((*my_ptr) && (b_ptr < (buffer + b_size)) && 
   (tokens_found <= p_size)) {
    ptrs[tokens_found] = b_ptr;
    while ((*my_ptr) && (*my_ptr != ' ') && (b_ptr<(buffer + b_size)))
      *b_ptr++ = *my_ptr++;
    *b_ptr++ = 0;
    while ((*my_ptr) && (*my_ptr == ' ')) ++my_ptr;
    ++tokens_found;
  }
  *argc1 = tokens_found;
  
  return(1);
}
#undef b_size
#undef p_size
/* routines to help out with circle and ellipse calculations */
/* set an arc, we get the positions of 1st pt, intermediate pt, end pt,
   all angles from x axis anticlockwise */
/* for equations we have:
   x[0] = xc + r * cos(theta0)
   y[0] = yc + r * sin(theta0)
   x[1] = xc + r * cos(theta1)
   y[1] = yc + r * sin(theta1)
   x[2] = xc + r * cos(theta2)
   y[2] = yc + r * sin(theta2)
   */
get_c_info(pt_ptr, xc, yc, r, theta, deg)
     int *pt_ptr, *xc, *yc, *r;
     float *deg;
     double *theta;
{
  int it1, i;
  double t1, rxc, ryc, rr, t2, t3, rsq, pi;
  
  /* first make sure they're not co-linear */
  it1 = (pt_ptr[4] - pt_ptr[0]) * (pt_ptr[3] - pt_ptr[1]) -
    (pt_ptr[2] - pt_ptr[0]) * (pt_ptr[5] - pt_ptr[1]);
  if (it1 == 0) return(0);  /* colinear */
  /* can get xc and yc now */
  t1 = 2.0 * it1;
  t2 = (pt_ptr[2] * pt_ptr[2] - pt_ptr[0] * pt_ptr[0]) +
    (pt_ptr[3] * pt_ptr[3] - pt_ptr[1] * pt_ptr[1]);
  t3 = (pt_ptr[4] * pt_ptr[4] - pt_ptr[0] * pt_ptr[0]) +
    (pt_ptr[5] * pt_ptr[5] - pt_ptr[1] * pt_ptr[1]);
  
  /* first yc */
  ryc = ((pt_ptr[4] - pt_ptr[0]) * t2 -
   (pt_ptr[2] - pt_ptr[0]) * t3) / t1;
  /* now xc */
  rxc = ((pt_ptr[5] - pt_ptr[1]) * t2 -
   (pt_ptr[5] - pt_ptr[1]) * t3) / t1;
  
  /* now pick up the radius */
  rsq = (pt_ptr[0] - rxc) * (pt_ptr[0] - rxc) +
    (pt_ptr[1] - ryc) * (pt_ptr[1] - ryc);
  
  rr= sqrt(rsq);  /* non-negative by definition */
  
  /* now the angles */
  pi = 4.0 * atan(1.0);
  for (i=0; i<3; ++i) {
    theta[i] = atan2((pt_ptr[2 * i + 1] - ryc) / rr, 
         (pt_ptr[2 * i] - rxc) / rr);
    deg[i] = 180.0 * theta[i] / pi;
  }
  /* finally the integer forms, do a simple truncate */  
  *xc = (int) rxc;
  *yc = (int) ryc;
  *r = (int) rr;
  return(1);
}
/* routine to take a vector and return a degree/radian, measured
   from the x axis in an anticlockwise direction */

int get_angle ( int *ptr, float *deg, double *theta )
{
  double pi, dx, dy;
  
  pi = 4.0 * atan(1.0);
  
  if (ptr[0] == 0) {
    if (ptr[1] >=0) {
      *deg = *theta = 0.0;
    } else {
      *deg = 180.0;
      *theta = pi;
    }
    return(2);
  }
  dx = (double) ptr[0];
  dy = (double) ptr[1];
  
  *theta = atan2(dy, dx);
  *deg = 180.0 * (*theta / pi);
  return(1);
}
/* function to return cos and sin corr. to x and y components */
angle_str(ctheta, stheta, y_comp, x_comp)
     double *ctheta, *stheta;
     int y_comp, x_comp;
{
  double hypotenuse, sqrt();
  
  if (!(y_comp || x_comp)) return(0);  /* unknown */
  
  if (!x_comp) {
    *ctheta = 0.0;
    if (y_comp > 0) *stheta = 1.0;
    else *stheta = -1.0;
    return(1);
  }
  if (!y_comp) {
    *stheta = 0.0;
    if (x_comp > 0) *ctheta = 1.0;
    else *ctheta = -1.0;
    return(1);
  }
  
  hypotenuse = y_comp * y_comp + x_comp * x_comp;
  hypotenuse = sqrt(hypotenuse);
  *ctheta = x_comp / hypotenuse;
  *stheta = y_comp / hypotenuse;
  return(1);
}
/* function to fill out the correct offsets */
get_offsets(dx, dy, ctheta1, stheta1, ctheta2, stheta2, text_align, ht, wd, 
      path)
     int *dx, *dy, ht, wd;
     double ctheta1, stheta1, ctheta2, stheta2;
     struct align_struct *text_align;
     enum path_enum path;
{
  int x_sz, y_sz;
  
  switch (text_align->ver) {
  case normal_v:
    switch (path) {
    case right:    *dy = 0; break;
    case left:    *dy = 0; break;
    case up:    *dy = 0; break;
    case down:    *dy = 0; break;
    }
    break;
  case top_v:    *dy = -1.1 * ht * stheta2; break;
  case cap_v:    *dy = ht; break;
  case half_v:    *dy = -0.5 * ht * stheta2; break;
  case base_v:    *dy = 0; break;
  case bottom_v:    *dy = 0.1 * ht * stheta2; break;
  case cont_v:    *dy = 0; break;
  }
  /* now add on x component */
  /*  *dy -= wd * stheta1; */
  
  switch (text_align->hor) {
  case normal_h:
    switch (path) {
    case right:    *dx = 0; break;
    case left:    *dx = 0; break;
    case up:
    case down:    *dx = -wd * 0.5 * ctheta1; break;
    }
    break;
  case left_h:    *dx = 0; break;
  case center_h:    *dx = -0.5 * wd * ctheta1; break;
  case right_h:    *dx = -wd * ctheta1; break;
  case cont_h:    *dx = 0; break;
  }
  return(1);
}
/* function to do any smart type things necessary to get the device ready */
void get_smart(started, gsptr1, gsptr2, opt, dev_info, cosr, sinr,
         pxl_vdc, xoffset, yoffset, xp0, yp0, xsize, ysize, font_check, 
         font_text, sx, sy)
     int started;
     struct mf_d_struct   *gsptr1;  /* the class 1 element pointer */
     struct pic_d_struct *gsptr2;  /* the class 2 element pointer */
     struct one_opt *opt;  /* the command line options, in only */
     struct info_struct *dev_info;  /* device info to fill out, out only */
     double *cosr, *sinr, *pxl_vdc, *sx, *sy;
     int *xoffset, *yoffset, *xsize, *ysize;
     float *xp0, *yp0;
     int (**font_check)(), (**font_text)(); 
{
  float vdc_y, vdc_x;
  double fabs(), cos(), sin();
  float vdc_w, vdc_h, x_scale, y_scale, pi;
  double theta;
  float dev_ht, dev_wd;  /* the device parameters */
  double in_vdc;  /* conversion from VDC units to inches */
  extern int ca_check(), ca_text(), hl_check(), hl_text(); /* font routines */ 
  float bl_x, bl_y;  /* bottom left x and y co-ords */
  char *namptr = NULL;
  int to_cgm = 0;
  /* get the device and VDC sizes */
  
  dev_wd = dev_info->x_size;
  dev_ht = dev_info->y_size;
  
  
  /* now sort out the vdc dimensions */
  
  switch (gsptr1->vdc_type) {
  case vdc_int: 
    if (gsptr2->vdc_extent.i[2] > gsptr2->vdc_extent.i[0]) {
      vdc_x = gsptr2->vdc_extent.i[2] - gsptr2->vdc_extent.i[0];
      bl_x = gsptr2->vdc_extent.i[0];
      *sx = 1.0;
    } else {
      vdc_x = gsptr2->vdc_extent.i[0] - gsptr2->vdc_extent.i[2];
      bl_x = gsptr2->vdc_extent.i[2];
      *sx = -1.0;
    }
    if (gsptr2->vdc_extent.i[3] > gsptr2->vdc_extent.i[1]) {
      vdc_y = gsptr2->vdc_extent.i[3] - gsptr2->vdc_extent.i[1];
      bl_y = gsptr2->vdc_extent.i[1];
      *sy = 1.0;
    } else {
      vdc_y = gsptr2->vdc_extent.i[1] - gsptr2->vdc_extent.i[3];
      bl_y = gsptr2->vdc_extent.i[1];
      *sy = -1.0;
    }
    break;
  case vdc_real: 
    if (gsptr2->vdc_extent.r[2] > gsptr2->vdc_extent.r[0]) {
      vdc_x = gsptr2->vdc_extent.r[2] - gsptr2->vdc_extent.r[0];
      bl_x = gsptr2->vdc_extent.r[0];
      *sx = 1.0;
    } else {
      vdc_x = gsptr2->vdc_extent.r[0] - gsptr2->vdc_extent.r[2];
      bl_x = gsptr2->vdc_extent.r[2];
      *sx = -1.0;
    }
    if (gsptr2->vdc_extent.r[3] > gsptr2->vdc_extent.r[1]) {
      vdc_y = gsptr2->vdc_extent.r[3] - gsptr2->vdc_extent.r[1];
      bl_y = gsptr2->vdc_extent.r[1];
      *sy = 1.0;
    } else {
      vdc_y = gsptr2->vdc_extent.r[1] - gsptr2->vdc_extent.r[3];
      bl_y = gsptr2->vdc_extent.r[1];
      *sy = -1.0;
    }
    break;
  default:    (void) fprintf(stderr, "illegal vdc_type = %d\n",gsptr1->vdc_type);
  }
  
  /* now take care of centering, etc */
  
  if ( (!opt[(int) degrees].set)   /* didn't specify a rotation */
      && (dev_info->capability & port_land)/* can rotate page */
      && (!opt[(int) screen].set)    /* not on a screen */
      && (!opt[(int) included].set)  /* not included */
      && (    (   (vdc_x > vdc_y)    /* wide image */
         && (dev_wd < dev_ht))  /* narrow page */
    ||  (   (vdc_x < vdc_y)    /* narrow image */
         &&  (dev_wd > dev_ht))  /* wide page */
    )
      ) {
    opt[(int) degrees].val.r = 90.0;   /* rotate it for the user */
    opt[(int) degrees].set = 1;
  }
  if (opt[(int) degrees].set) {  /* specified/given a rotation */
    pi = 4. * atan(1.0);
    theta = pi * opt[(int) degrees].val.r / 180.0 ;
    *cosr = cos(theta);
    *sinr = sin(theta);
  } else {  /* standard */
    *cosr = 1.0;
    *sinr = 0.0;
  }
  /* these are the real height and width of the vdc image */
  vdc_w = fabs(vdc_x * (*cosr)) + fabs(vdc_y * (*sinr));
  vdc_h = fabs(vdc_x * (*sinr)) + fabs(vdc_y * (*cosr));
  
  /* now figure out the scaling */
  
  if ((gsptr2->scale_mode.s_mode == 1) &&    /* metric scaling */
      !opt[(int) scale_output].set) {    /* not overridden */
    in_vdc = gsptr2->scale_mode.m_scaling / 25.4;
    x_scale = y_scale = in_vdc;
    *pxl_vdc = dev_info->pxl_in * in_vdc;
    *xsize = ((dev_wd * dev_info->pxl_in) < (*pxl_vdc * vdc_w)) ? 
      dev_wd * dev_info->pxl_in : *pxl_vdc * vdc_w;
    *ysize = ((dev_ht * dev_info->ypxl_in) < (*pxl_vdc * vdc_h)) ? 
      dev_ht * dev_info->ypxl_in : *pxl_vdc * vdc_h;
  } else {
    x_scale = dev_wd / vdc_w;
    y_scale = dev_ht / vdc_h;
    /* which is the smallest ? */
    if (x_scale < y_scale) {
      *xsize = dev_wd * dev_info->pxl_in;
      *ysize = dev_ht * (x_scale / y_scale) * dev_info->ypxl_in;
      in_vdc = y_scale = x_scale;
    } else {
      *xsize = dev_wd * (y_scale / x_scale) * dev_info->pxl_in;
      *ysize = dev_ht * dev_info->ypxl_in;
      in_vdc = x_scale = y_scale;
    }
    *pxl_vdc = dev_info->pxl_in * in_vdc;
  }
  if (!started) return;
  
  /* get the origin to the right place */
  if ((*cosr >= 0.0) && (*sinr >= 0.0)) {
    *xoffset = dev_info->pxl_in * in_vdc * vdc_y * (*sinr);
    *yoffset = 0.0;
  } else 
    if ((*cosr < 0.0) && (*sinr >= 0.0)) {
      *xoffset = dev_info->pxl_in * in_vdc * 
  (vdc_y * (*sinr) - vdc_x * (*cosr));
      *yoffset = - dev_info->ypxl_in * in_vdc * vdc_x * (*cosr);
    } else 
      if ((*cosr < 0.0) && (*sinr < 0.0)) {
  *xoffset = - dev_info->pxl_in * in_vdc * vdc_x * (*cosr);
  *yoffset = - dev_info->ypxl_in * in_vdc * 
    (vdc_x * (*sinr) + vdc_y * (*cosr));
      } else 
  if ((*cosr >= 0.0) && (*sinr < 0.0)) {
    *xoffset = 0.0;
    *yoffset = - dev_info->ypxl_in * in_vdc * vdc_x * (*sinr);
  }
  if (!gsptr2->scale_mode.s_mode && !opt[(int) degrees].set) {    
    *xoffset = (dev_wd - vdc_x * in_vdc) * dev_info->pxl_in / 2;
  }
  /* fix later PLA */
  /* and add any externally required offsets */
  *xoffset += dev_info->pxl_in * dev_info->x_offset;
  *yoffset += dev_info->ypxl_in * dev_info->y_offset;
  
  /* may be able to do some stuff at device level */
  
  /* if we're going to another cgm file, no internal offsets */
  if (opt[(int) device].set) namptr = opt[(int) device].val.str;
  else if (opt[(int) screen].set) namptr = opt[(int) screen].val.str;
  to_cgm = namptr &&
    ((*namptr == 'C') || (*namptr == 'c')) &&
      ((namptr[1] == 'G') || (namptr[1] == 'g')) &&
  ((namptr[2] == 'M') || (namptr[2] == 'm'));
  
  if (to_cgm) {  /* pass everything thru */
    *xp0 = 0;
    *yp0 = 0;
    *sx = 1.0;
    *sy = 1.0;
    *pxl_vdc = (gsptr1->vdc_type == vdc_int) ? 1.0 :
      32767.0 / dev_wd;
  } else if (dev_info->capability & arb_trans) { /*  device can do it */
    *xp0 = - *pxl_vdc * bl_x;
    *yp0 = - *pxl_vdc * bl_y;
  } else {
    *xp0 = *xoffset - *pxl_vdc * bl_x;
    *yp0 = *yoffset - *pxl_vdc * bl_y;
  }
  if (dev_info->capability & arb_rot){ /* device can do any rotn */
    *cosr = 1.0;
    *sinr = 0.0;
  } else if ((dev_info->capability & port_land) &&
       (opt[(int) degrees].val.r < 91.0) &&
       (opt[(int) degrees].val.r > 89.0)) { /* this only */
    *cosr = 1.0;
    *sinr = 0.0;
    *xp0 = - *pxl_vdc * bl_y + dev_info->pxl_in * dev_info->x_offset;
    *yp0 = - *pxl_vdc * bl_x + dev_info->ypxl_in * dev_info->y_offset;
  }
  /* now take care of the font functions */
#ifdef incpkras    /* linked with pk fonts */
  if (opt[(int) font_type].set && 
      (opt[(int) font_type].val.i == 2)) {
    *font_check = ca_check;
    *font_text = ca_text;
  }
#endif
#ifdef inchershey    /* linked with hershey fonts */
  if ((opt[(int) font_type].set && 
       (opt[(int) font_type].val.i == 1)) ||
      (new_opt && !new_opt[(int) font_type].set)) {
    *font_check = hl_check;
    *font_text = hl_text;
  }
#endif
  return;
}
/* handle clipping (very simpleminded) */
int uclip(xin, yin, clip_rect)
int *xin, *yin, *clip_rect;
{
  if ((*xin - clip_rect[0]) * (*xin - clip_rect[2]) > 0) { /* needs clipping */
    *xin = ((*xin - clip_rect[0]) * (*xin - clip_rect[0]) <
      (*xin - clip_rect[2]) * (*xin - clip_rect[2])) ?
        clip_rect[0] : clip_rect[2];
  }
  if ((*yin - clip_rect[1]) * (*yin - clip_rect[3]) > 0) { /* needs clipping */
    *yin = ((*yin - clip_rect[1]) * (*yin - clip_rect[1]) <
      (*yin - clip_rect[3]) * (*yin - clip_rect[3])) ?
        clip_rect[1] : clip_rect[3];
  }
  return 1;
}