text: /*                          N I R T . C
   * BRL-CAD
   *
   * Copyright (c) 2004-2016 United States Government as represented by
   * the U.S. Army Research Laboratory.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public License
   * version 2.1 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this file; see the file named COPYING for more
   * information.
   */
  /** @file nirt/nirt.c
   *
   * This program is Natalie's Interactive Ray-Tracer
   *
   */
  
  #include "common.h"
  
  #include <stdlib.h>
  #include <ctype.h>
  #include <math.h>
  #include <string.h>
  #include "bio.h"
  
  #include "bu/env.h"
  #include "bu/getopt.h"
  #include "bu/list.h"
  #include "bu/units.h"
  #include "bu/path.h"
  #include "vmath.h"
  #include "raytrace.h"
  
  /* private */
  #include "./nirt.h"
  #include "./usrfmt.h"
  #include "brlcad_ident.h"
  
  
  /* bleh */
  int need_prep = 1;
  
  const com_table ComTab[] = {
      { "attr", cm_attr, "select attributes", "<-f(flush) | -p(print) | attribute_name>" },
      { "ae", az_el, "set/query azimuth and elevation", "azimuth elevation" },
      { "dir", dir_vect, "set/query direction vector", "x-component y-component z-component" },
      { "hv", grid_coor, "set/query gridplane coordinates", "horz vert [dist]" },
      { "xyz", target_coor, "set/query target coordinates", "X Y Z" },
      { "s", shoot, "shoot a ray at the target", NULL },
      { "backout", backout, "back out of model", NULL },
      { "useair", use_air, "set/query use of air", "<0|1|2|...>" },
      { "units", nirt_units, "set/query local units", "<mm|cm|m|in|ft>" },
      { "overlap_claims", do_overlap_claims, "set/query overlap rebuilding/retention", "<0|1|2|3>" },
      { "fmt", format_output, "set/query output formats", "{rhpfmog} format item item ..." },
      { "dest", direct_output, "set/query output destination", "file/pipe" },
      { "statefile", state_file, "set/query name of state file", "file" },
      { "dump", dump_state, "write current state of NIRT to the state file", NULL },
      { "load", load_state, "read new state for NIRT from the state file", NULL },
      { "print", print_item, "query an output item", "item" },
      { "bot_minpieces", bot_minpieces, "Get/Set value for rt_bot_minpieces (0 means do not use pieces, default is 32)", "min_pieces" },
      { "libdebug", cm_libdebug, "set/query librt debug flags", "hex_flag_value" },
      { "debug", cm_debug, "set/query nirt debug flags", "hex_flag_value" },
      { "q", quit, "quit", NULL },
      { "?", show_menu, "display this help menu", NULL },
      { (char *)NULL, NULL, (char *)NULL, (char *)NULL }
  };
  
  
  struct script_rec
  {
      struct bu_list l;
      int sr_type;	/* Direct or indirect */
      struct bu_vls sr_script;	/* Literal or file name */
  };
  #define SCRIPT_REC_NULL ((struct script_rec *) 0)
  #define SCRIPT_REC_MAGIC 0x73637270
  #define sr_magic l.magic
  
  
  int do_backout = 0;			/* Backout before shooting? */
  int overlap_claims = OVLP_RESOLVE;	/* Rebuild/retain overlaps? */
  char *ocname[4];
  int silent_flag = SILENT_UNSET;	/* Refrain from babbling? */
  int nirt_debug = 0;			/* Control of diagnostics */
  
  /* Parallel structures needed for operation w/ and w/o air */
  struct rt_i *rti_tab[2];
  struct resource res_tab;
  
  struct application ap;
  
  attr_table a_tab;
  
  /* the name of the BRL-CAD geometry file */
  char *db_name;
  
  
  void printusage(void)
  {
      bu_log("Usage: 'nirt [options] model.g objects...'\n");
      bu_log("Options:\n");
      bu_log(" -A n       add attribute_name=n\n");
      bu_log(" -M         read matrix, cmds on stdin\n");
      bu_log(" -b         back out of geometry before first shot\n");
      bu_log(" -B n       set rt_bot_minpieces=n\n");
      bu_log(" -T n       set rt_bot_mintie=n (deprecated, use LIBRT_BOT_MINTIE instead)\n");
      bu_log(" -e script  run script before interacting\n");
      bu_log(" -f sfile   run script sfile before interacting\n");
      bu_log(" -E         ignore any -e or -f options specified earlier on the command line\n");
      bu_log(" -L         list output formatting options\n");
      bu_log(" -s         run in silent (non-verbose) mode\n");
      bu_log(" -v         run in verbose mode\n");
      bu_log(" -H n       flag (n) for enable/disable informational header\n");
      bu_log("            (n=1 [on] by default, always off in silent mode)\n");
      bu_log(" -u n       set use_air=n (default 0)\n");
      bu_log(" -O action  handle overlap claims via action\n");
      bu_log(" -x v       set librt(3) diagnostic flag=v\n");
      bu_log(" -X v       set nirt diagnostic flag=v\n");
  }
  
  /**
   * List formats installed in global nirt data directory
   */
  size_t
  listformats(char ***names)
  {
      size_t files, i;
      char **filearray = NULL;
      char suffix[6]="*.nrt";
      FILE *cfPtr = NULL;
      int fnddesc;
  
      struct bu_vls nirtfilespath = BU_VLS_INIT_ZERO;
      struct bu_vls nirtpathtofile = BU_VLS_INIT_ZERO;
      struct bu_vls vlsfileline = BU_VLS_INIT_ZERO;
  
      /* get a nirt directory listing */
      bu_vls_printf(&nirtfilespath, "%s", bu_brlcad_data("nirt", 0));
      files = bu_file_list(bu_vls_addr(&nirtfilespath), suffix, &filearray);
      if (names)
  	*names = filearray;
  
      /* open every nirt file we find, extract, and print the description */
      for (i = 0; i < files && !names; i++) {
  	bu_vls_trunc(&nirtpathtofile, 0);
  	bu_vls_printf(&nirtpathtofile, "%s/%s", bu_vls_addr(&nirtfilespath), filearray[i]);
  	cfPtr = fopen(bu_vls_addr(&nirtpathtofile), "rb");
  
  	fnddesc = 0;
  	bu_vls_trunc(&vlsfileline, 0);
  	while (bu_vls_gets(&vlsfileline, cfPtr) && fnddesc == 0) {
  	    if (bu_strncmp(bu_vls_addr(&vlsfileline), "# Description: ", 15) == 0) {
  		fnddesc = 1;
  		bu_log("%s\n", bu_vls_addr(&vlsfileline)+15);
  	    }
  	    bu_vls_trunc(&vlsfileline, 0);
  	}
  	fclose(cfPtr);
      }
  
      /* release resources */
      if (!names)
  	bu_argv_free(files, filearray);
      bu_vls_free(&vlsfileline);
      bu_vls_free(&nirtfilespath);
      bu_vls_free(&nirtpathtofile);
  
      return files;
  }
  
  void
  attrib_print(void)
  {
      int i;
  
      for (i = 0; i < a_tab.attrib_use; i++) {
  	fprintf(stdout, "\"%s\"\n", a_tab.attrib[i]);
      }
  }
  
  
  /**
   * flush the list of desired attributes
   */
  void
  attrib_flush(void)
  {
      int i;
  
      a_tab.attrib_use = 0;
      for (i = 0; i < a_tab.attrib_use; i++)
  	bu_free(a_tab.attrib[i], "strdup");
  }
  
  
  void
  attrib_add(char *a, int *prep)
  {
      char *p;
  
      if (!a) {
  	bu_log("attrib_add null arg\n");
  	return; /* null char ptr */
      }
  
      p = strtok(a, "\t ");
      while (p) {
  
  	/* make sure we have space */
  	if (!a_tab.attrib || a_tab.attrib_use >= (a_tab.attrib_cnt-1)) {
  	    a_tab.attrib_cnt += 16;
  	    a_tab.attrib = (char **)bu_realloc(a_tab.attrib,
  					       a_tab.attrib_cnt * sizeof(char *),
  					       "attrib_tab");
  	}
  
  	/* add the attribute name(s) */
  	a_tab.attrib[a_tab.attrib_use] = bu_strdup(db5_standard_attribute(db5_standardize_attribute(p)));
  	/* bu_log("attrib[%d]=\"%s\"\n", attrib_use, attrib[attrib_use]); */
  	a_tab.attrib[++a_tab.attrib_use] = (char *)NULL;
  
  	p = strtok((char *)NULL, "\t ");
  	*prep = 1;
      }
  }
  
  
  /**
   * string is a literal or a file name
   */
  static void
  enqueue_script(struct bu_list *qp, int type, char *string)
  {
      struct script_rec *srp;
      FILE *cfPtr;
      struct bu_vls str = BU_VLS_INIT_ZERO;
  
      BU_CK_LIST_HEAD(qp);
  
      BU_ALLOC(srp, struct script_rec);
      srp->sr_magic = SCRIPT_REC_MAGIC;
      srp->sr_type = type;
      bu_vls_init(&(srp->sr_script));
  
      /*Check if supplied file name is local or in brlcad's nirt data dir*/
      if (type == READING_FILE) {
  	bu_vls_trunc(&str, 0);
  	bu_vls_printf(&str, "%s", string);
  	cfPtr = fopen(bu_vls_addr(&str), "rb");
  	if (cfPtr == NULL) {
  	    bu_vls_trunc(&str, 0);
  	    bu_vls_printf(&str, "%s/%s.nrt", bu_brlcad_data("nirt", 0), string);
  	    cfPtr = fopen(bu_vls_addr(&str), "rb");
  	    if (cfPtr != NULL) {
  		fclose(cfPtr);
  	    } else {
  		bu_vls_trunc(&str, 0);
  		bu_vls_printf(&str, "%s", string);
  	    }
  	} else {
  	    fclose(cfPtr);
  	}
  	bu_vls_printf(&(srp->sr_script), "%s", bu_vls_addr(&str));
      } else {
  	bu_vls_strcat(&(srp->sr_script), string);
      }
      BU_LIST_INSERT(qp, &(srp->l));
      bu_vls_free(&str);
  }
  
  
  /**
   * text is for the title line
   */
  static void
  show_scripts(struct bu_list *sl, char *text)
  {
      int i;
      struct script_rec *srp;
  
      BU_CK_LIST_HEAD(sl);
  
      i = 0;
      bu_log("- - - - - - - The command-line scripts - - - - - - -\n%s\n", text);
      for (BU_LIST_FOR(srp, script_rec, sl)) {
  	BU_CKMAG(srp, SCRIPT_REC_MAGIC, "script record");
  
  	bu_log("%d. script %s '%s'\n",
  	       ++i,
  	       (srp->sr_type == READING_STRING) ? "string" :
  	       (srp->sr_type == READING_FILE) ? "file" : "???",
  	       bu_vls_addr(&(srp->sr_script)));
      }
      bu_log("- - - - - - - - - - - - - - - - - - - - - - - - - -\n");
  }
  
  
  static void
  free_script(struct script_rec *srp)
  {
      BU_CKMAG(srp, SCRIPT_REC_MAGIC, "script record");
  
      bu_vls_free(&(srp->sr_script));
      bu_free((void *) srp, "script record");
  }
  
  
  static void
  run_scripts(struct bu_list *sl, struct rt_i *rtip)
  {
      struct script_rec *srp;
      char *cp;
      FILE *fPtr;
  
      if (nirt_debug & DEBUG_SCRIPTS)
  	show_scripts(sl, "before running them");
  
      while (BU_LIST_WHILE(srp, script_rec, sl)) {
  	BU_LIST_DEQUEUE(&(srp->l));
  	BU_CKMAG(srp, SCRIPT_REC_MAGIC, "script record");
  	cp = bu_vls_addr(&(srp->sr_script));
  
  	if (nirt_debug & DEBUG_SCRIPTS) {
  	    bu_log("  Attempting to run %s '%s'\n",
  		   (srp->sr_type == READING_STRING) ? "literal" :
  		   (srp->sr_type == READING_FILE) ? "file" : "???",
  		   cp);
  	}
  
  	switch (srp->sr_type) {
  	    case READING_STRING:
  		interact(READING_STRING, cp, rtip);
  		break;
  	    case READING_FILE:
  		if ((fPtr = fopen(cp, "rb")) == NULL) {
  		    bu_log("Cannot open script file '%s'\n", cp);
  		} else {
  		    interact(READING_FILE, fPtr, rtip);
  		    fclose(fPtr);
  		}
  		break;
  	    default:
  		bu_exit (1, "%s:%d: script of type %d.  This shouldn't happen\n", __FILE__, __LINE__, srp->sr_type);
  	}
  	free_script(srp);
      }
  
      if (nirt_debug & DEBUG_SCRIPTS)
  	show_scripts(sl, "after running them");
  }
  
  
  int
  main(int argc, char *argv[])
  {
      struct rt_i *rtip = NULL;
  
      char db_title[TITLE_LEN+1];/* title from MGED file */
      const char *tmp_str;
      extern char local_u_name[65];
      extern double base2local;
      extern double local2base;
      FILE *fPtr;
      int Ch;		/* Option name */
      int mat_flag = 0;	/* Read matrix from stdin? */
      int use_of_air = 0;
      int print_ident_flag = 1;
  #define OCASTRING_LEN 1024
      char ocastring[OCASTRING_LEN+1] = {0};
      struct bu_list script_list;	/* For -e and -f options */
      struct script_rec *srp;
      extern outval ValTab[];
  
      /* from if.c, callback functions for overlap, hit, and miss shots */
      int if_overlap(struct application *, struct partition *, struct region *, struct region *, struct partition *);
      int if_hit(struct application *, struct partition *, struct seg *);
      int if_miss(struct application *);
  
      BU_LIST_INIT(&script_list);
  
      bu_setprogname(argv[0]);
  
      ocname[OVLP_RESOLVE] = "resolve";
      ocname[OVLP_REBUILD_FASTGEN] = "rebuild_fastgen";
      ocname[OVLP_REBUILD_ALL] = "rebuild_all";
      ocname[OVLP_RETAIN] = "retain";
      *ocastring = '\0';
  
      bu_optind = 1;		/* restart */
  
      /* Handle command-line options */
      while ((Ch = bu_getopt(argc, argv, OPT_STRING)) != -1) {
      	if (bu_optopt == '?') Ch='h';
  	switch (Ch) {
  	    case 'A':
  		attrib_add(bu_optarg, &need_prep);
  		break;
  	    case 'B':
  		rt_bot_minpieces = atoi(bu_optarg);
  		break;
  	    case 'T':
  		bu_setenv("LIBRT_BOT_MINTIE", bu_optarg, 1);
  		break;
  	    case 'b':
  		do_backout = 1;
  		break;
  	    case 'E':
  		if (nirt_debug & DEBUG_SCRIPTS)
  		    show_scripts(&script_list, "before erasure");
  		while (BU_LIST_WHILE(srp, script_rec, &script_list)) {
  		    BU_LIST_DEQUEUE(&(srp->l));
  		    free_script(srp);
  		}
  		if (nirt_debug & DEBUG_SCRIPTS)
  		    show_scripts(&script_list, "after erasure");
  		break;
  	    case 'e':
  		enqueue_script(&script_list, READING_STRING, bu_optarg);
  		if (nirt_debug & DEBUG_SCRIPTS)
  		    show_scripts(&script_list, "after enqueueing a literal");
  		break;
  	    case 'f':
  		enqueue_script(&script_list, READING_FILE, bu_optarg);
  		if (nirt_debug & DEBUG_SCRIPTS)
  		    show_scripts(&script_list, "after enqueueing a file name");
  		break;
  	    case 'L':
  		listformats(NULL);
  		bu_exit(EXIT_SUCCESS, NULL);
  	    case 'M':
  		mat_flag = 1;
  		break;
  	    case 'O':
  		sscanf(bu_optarg, CPP_SCAN(OCASTRING_LEN), ocastring);
  		break;
  	    case 's':
  		silent_flag = SILENT_YES;	/* Positively yes */
  		break;
  	    case 'v':
  		silent_flag = SILENT_NO;	/* Positively no */
  		break;
  	    case 'x':
  		sscanf(bu_optarg, "%x", (unsigned int *)&RTG.debug);
  		break;
  	    case 'X':
  		sscanf(bu_optarg, "%x", (unsigned int *)&nirt_debug);
  		break;
  	    case 'u':
  		if (sscanf(bu_optarg, "%d", &use_of_air) != 1) {
  		    (void) fprintf(stderr,
  				   "Illegal use-air specification: '%s'\n", bu_optarg);
  		    return 1;
  		}
  		break;
  	    case 'H':
  		if (sscanf(bu_optarg, "%d", &print_ident_flag) != 1) {
  		    (void) fprintf(stderr,
  				   "Illegal header output option specified: '%s'\n", bu_optarg);
  		    return 1;
  		}
  		break;
  	    default:
  		printusage();
  		bu_exit (Ch != 'h', NULL);
  	}
      } /* end while getopt */
  
      if (argc - bu_optind < 2) {
  	printusage();
  	return 1;
      }
  
      if (isatty(0)) {
  	if (silent_flag != SILENT_YES)
  	    silent_flag = SILENT_NO;
      } else {
  	/* stdin is not a TTY */
  	if (silent_flag != SILENT_NO)
  	    silent_flag = SILENT_YES;
      }
      if (silent_flag != SILENT_YES && print_ident_flag)
  	(void) fputs(brlcad_ident("Natalie's Interactive Ray Tracer"), stdout);
  
      if (use_of_air && (use_of_air != 1)) {
  	fprintf(stderr,
  		"Warning: useair=%d specified, will set to 1\n", use_of_air);
  	use_of_air = 1;
      }
  
      switch (*ocastring) {
  	case '\0':
  	    overlap_claims = OVLP_RESOLVE;
  	    break;
  	case '0':
  	case '1':
  	case '2':
  	case '3':
  	    if (ocastring[1] == '\0') {
  		sscanf(ocastring, "%d", &overlap_claims);
  	    } else {
  		fprintf(stderr,
  			"Illegal overlap_claims specification: '%s'\n", ocastring);
  		return 1;
  	    }
  	    break;
  	case 'r':
  	    if (BU_STR_EQUAL(ocastring, "resolve"))
  		overlap_claims = OVLP_RESOLVE;
  	    else if (BU_STR_EQUAL(ocastring, "rebuild_fastgen"))
  		overlap_claims = OVLP_REBUILD_FASTGEN;
  	    else if (BU_STR_EQUAL(ocastring, "rebuild_all"))
  		overlap_claims = OVLP_REBUILD_ALL;
  	    else if (BU_STR_EQUAL(ocastring, "retain"))
  		overlap_claims = OVLP_RETAIN;
  	    else {
  		fprintf(stderr,
  			"Illegal overlap_claims specification: '%s'\n", ocastring);
  		return 1;
  	    }
  	    break;
  	default:
  	    fprintf(stderr,
  		    "Illegal overlap_claims specification: '%s'\n", ocastring);
  	    return 1;
      }
  
      db_name = argv[bu_optind];
  
      /* let users know that other output styles are available */
      if (silent_flag != SILENT_YES) {
  	const char *default_name = "default";
  	char **names = NULL;
  	size_t fmtcnt, i;
  	printf("Output format:");
  	if (bu_list_len(&script_list) == 0) {
  	    printf(" %s", default_name);
  	} else {
  	    struct bu_vls name = BU_VLS_INIT_ZERO;
  
  	    /* last script listed wins */
  	    for (BU_LIST_FOR(srp, script_rec, &script_list)) {
  		if (srp->sr_type == READING_FILE) {
  		    bu_path_component(&name, bu_vls_addr(&(srp->sr_script)), BU_PATH_BASENAME_EXTLESS);
  		}
  	    }
  
  	    /* maybe no files, only execution scripts, so default output */
  	    if (bu_vls_strlen(&name) == 0)
  		bu_vls_printf(&name, default_name);
  
  	    printf(" %s", bu_vls_addr(&name));
  	    bu_vls_free(&name);
  	}
  	printf(" (specify -L option for descriptive listing)\n");
  
  	i = 0;
  	fmtcnt = listformats(&names);
  	if (fmtcnt > 0) {
  	    printf("Formats available:");
  	    do {
  		char *dot = strchr(names[i], '.');
  		if (dot) /* trim off any filename suffix */
  		    *dot = '\0';
  		printf(" %s", names[i]);
  	    } while (++i < fmtcnt);
  
  	    printf(" (specify via -f option)\n");
  	}
  	bu_argv_free(fmtcnt, names);
      }
  
      /* build directory for target object */
      if (silent_flag != SILENT_YES) {
  	printf("Database file:  '%s'\n", db_name);
  	printf("Building the directory...");
      }
      if ((rtip = rt_dirbuild(db_name, db_title, TITLE_LEN)) == RTI_NULL) {
  	fflush(stdout);
  	fprintf(stderr, "Could not load file %s\n", db_name);
  	return 1;
      }
  
      rti_tab[use_of_air] = rtip;
      rti_tab[1 - use_of_air] = RTI_NULL;
      rtip->useair = use_of_air;
      rtip->rti_save_overlaps = (overlap_claims > 0);
  
      ++bu_optind;
      do_rt_gettrees(rtip, argv + bu_optind, argc - bu_optind, &need_prep);
  
      /* Initialize the table of resource structures */
      rt_init_resource(&res_tab, 0, rtip);
  
      /* initialization of the application structure */
      RT_APPLICATION_INIT(&ap);
      ap.a_hit = if_hit;        /* branch to if_hit routine */
      ap.a_miss = if_miss;      /* branch to if_miss routine */
      ap.a_overlap = if_overlap;/* branch to if_overlap routine */
      ap.a_logoverlap = rt_silent_logoverlap;
      ap.a_onehit = 0;          /* continue through shotline after hit */
      ap.a_resource = &res_tab;
      ap.a_purpose = "NIRT ray";
      ap.a_rt_i = rtip;         /* rt_i pointer */
      ap.a_zero1 = 0;           /* sanity check, sayth raytrace.h */
      ap.a_zero2 = 0;           /* sanity check, sayth raytrace.h */
      ap.a_uptr = (void *)a_tab.attrib;
  
      /* initialize variables */
      azimuth() = 0.0;
      elevation() = 0.0;
      direct(X) = -1.0;
      direct(Y) = 0.0;
      direct(Z) = 0.0;
      grid(HORZ) = 0.0;
      grid(VERT) = 0.0;
      grid(DIST) = 0.0;
      grid2targ();
      set_diameter(rtip);
  
      /* initialize the output specification */
      default_ospec();
  
      /* initialize NIRT's local units */
      base2local = rtip->rti_dbip->dbi_base2local;
      local2base = rtip->rti_dbip->dbi_local2base;
      tmp_str = bu_units_string(local2base);
      if (tmp_str) {
  	bu_strlcpy(local_u_name, tmp_str, sizeof(local_u_name));
      } else {
  	bu_strlcpy(local_u_name, "Unknown units", sizeof(local_u_name));
      }
  
      if (silent_flag != SILENT_YES) {
  	printf("Database title: '%s'\n", db_title);
  	printf("Database units: '%s'\n", local_u_name);
  	printf("model_min = (%g, %g, %g)    model_max = (%g, %g, %g)\n",
  	       rtip->mdl_min[X] * base2local,
  	       rtip->mdl_min[Y] * base2local,
  	       rtip->mdl_min[Z] * base2local,
  	       rtip->mdl_max[X] * base2local,
  	       rtip->mdl_max[Y] * base2local,
  	       rtip->mdl_max[Z] * base2local);
      }
  
      /* Run the run-time configuration file, if it exists */
      if ((fPtr = fopenrc()) != NULL) {
  	interact(READING_FILE, fPtr, rtip);
  	fclose(fPtr);
      }
  
      /* Run all scripts specified on the command line */
      run_scripts(&script_list, rtip);
  
      /* Perform the user interface */
      if (mat_flag) {
  	read_mat(rtip);
  	return 0;
      } else {
  	interact(READING_FILE, stdin, rtip);
      }
  
      return 0;
  }
  
  
  void
  do_rt_gettrees(struct rt_i *my_rtip, char **object_name, int nm_objects, int *prep)
  {
      static char **prev_names = 0;
      static int prev_nm = 0;
      int i;
  
      if (object_name == NULL) {
  	if ((object_name = prev_names) == 0)
  	    bu_exit (1, "%s:%d: This shouldn't happen\n", __FILE__, __LINE__);
  	nm_objects = prev_nm;
      }
  
      if (prev_names == 0) {
  	prev_names = object_name;
  	prev_nm = nm_objects;
      }
  
      if (silent_flag != SILENT_YES) {
  	printf("\nGet trees...");
  	fflush(stdout);
      }
  
      i = rt_gettrees_and_attrs(my_rtip, (const char **)a_tab.attrib, nm_objects, (const char **) object_name, 1);
      if (i) {
  	fflush(stdout);
  	bu_exit (1, "rt_gettrees() failed\n");
      }
  
      if (*prep) {
  	if (silent_flag != SILENT_YES) {
  	    printf("\nPrepping the geometry...");
  	    fflush(stdout);
  	}
  	rt_prep(my_rtip);
  	*prep = 0;
      }
  
      if (silent_flag != SILENT_YES) {
  	printf("\n%s", (nm_objects == 1) ? "Object" : "Objects");
  	for (i = 0; i < nm_objects; ++i)
  	    printf(" '%s'", object_name[i]);
  	printf(" processed\n");
      }
  }
  
  
  /*
   * Local Variables:
   * mode: C
   * tab-width: 8
   * indent-tabs-mode: t
   * c-file-style: "stroustrup"
   * End:
   * ex: shiftwidth=4 tabstop=8
   */
  