int
main(int argc, char *argv[]) 
{ 
  /* Setup the input data structures handlers */
  int format_output = 0;
  int strict_tags = 0;
  int echo_settings = 0;
  int dump_args = 0 ; /* set to 1 if dumping arguments to choose_primers */
  int io_version = 4;

  /* Some space for file names */
  char p3_all_file[FILE_NAME_SIZE];
  char p3_settings_file[FILE_NAME_SIZE];

  p3_global_settings *global_pa;
  seq_args *sarg;
  read_boulder_record_results read_boulder_record_res = {0,0};

  pr_append_str fatal_parse_err;
  pr_append_str nonfatal_parse_err;
  pr_append_str warnings;

  /* Some variables needed by getopt */
  int opt, option_index = 0;
  struct option long_options[] = {
    {"about", no_argument, 0, 'a'},
    {"format_output", no_argument, &format_output, 1},
    {"strict_tags", no_argument, &strict_tags, 1},
    {"p3_settings_file", required_argument, 0, 'p'},
    {"echo_settings_file", no_argument, &echo_settings, 1},
    {"io_version", required_argument, 0, 'i'},
    {"2x_compat", no_argument, 0, '2'},
    {"output", required_argument, 0, 'o'},
    {"error", required_argument, 0, 'e'},
    {0, 0, 0, 0}
  };
  int about = 0, output = 0, error = 0, compat = 0, invalid_flag = 0;
  char output_file[FILE_NAME_SIZE], error_file[FILE_NAME_SIZE]; 
 
  /* Retval will point to the return value from choose_primers(). */
  p3retval *retval = NULL;
  int input_found=0;

  p3_all_file[0] = '\0';
  p3_settings_file[0] = '\0';

  init_pr_append_str(&fatal_parse_err);
  init_pr_append_str(&nonfatal_parse_err);
  init_pr_append_str(&warnings);

  /* Get the program name for correct error messages */
  pr_release = libprimer3_release();
  pr_program_name = argv[0];
  p3_set_program_name(pr_program_name);

  /* We set up some signal handlers in case someone starts up the program
   * from the command line, wonders why nothing is happening, and then kills
   * the program. */
  signal(SIGINT, sig_handler);
  signal(SIGTERM, sig_handler);

  /* Allocate the space for global settings and fill in default parameters */
  global_pa = p3_create_global_settings();
  if (!global_pa) {
    exit(-2); /* Out of memory. */
  }
  if (dump_args) global_pa->dump = 1 ;
  
  /* Read in the flags provided with the program call */
  opterr = 0;
  while ((opt = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) {
    switch (opt) {
    case 'a':
      about = 1;
      break;
    case 'p':
      strncpy(p3_settings_file, optarg, FILE_NAME_SIZE - 1);
      break;
    case 'i':
      if (!strcmp(optarg, "3"))
        io_version = 3;
      else if (!strcmp(optarg, "4"))
        io_version = 4;
      else
        io_version = -1;
      break;
    case '2':
      compat = 1;
      break;
    case 'o':
      output = 1;
      strncpy(output_file, optarg, FILE_NAME_SIZE - 1);
      break;
    case 'e':
      error = 1;
      strncpy(error_file, optarg, FILE_NAME_SIZE - 1);
      break;
    case '?':
      invalid_flag = 1;
      break;
    }
  }
  /* Open the output and error files specified */
  if (error == 1) {
    /* reassign stderr */
    if (freopen(error_file, "w", stderr) == NULL) {
      fprintf(stderr, "Error creating file %s\n", error_file);
      exit(-1);
    }
  }
  if (output == 1) {
    /* reassign stdout */
    if (freopen(output_file, "w", stdout) == NULL) {
      fprintf(stderr, "Error creating file %s\n", output_file);
      exit(-1);
    }
  }
  /* We do any printing after redirecting stdout and stderr */
  if (about == 1) {
    printf("%s\n", pr_release);
    exit(0);
  }
  if ((io_version == -1) || (invalid_flag == 1)) {
    print_usage();
    exit(-1);
  }
  if (compat == 1) {
    printf("PRIMER_ERROR=flag -2x_compat is no longer supported\n=\n");
    exit(-1);
  }
  /* Check if an input file has been specified */
  if (optind < argc) {
    if (optind + 1 != argc) {
      print_usage();
      exit(-1);
    }
    if (freopen(argv[optind], "r", stdin) == NULL) {
      fprintf(stderr, "Error opening file %s\n", argv[optind]);
      exit(-1);
    }
  }

  /* Settings files have to be read in just below, and
     the functions need a temporary sarg */
  if (!(sarg = create_seq_arg())) {
    exit(-2);
  }

  /* Read data from the settings file until a "=" line occurs.  Assign parameter
   * values for primer picking to pa and sa. */
  if (p3_settings_file[0] != '\0') {
    read_p3_file(p3_settings_file, settings, echo_settings && !format_output,
		 global_pa, sarg, &fatal_parse_err, 
		 &nonfatal_parse_err, &warnings, &read_boulder_record_res);
    /* Check if the thermodynamical alignment flag was given */ 
    if (global_pa->thermodynamic_alignment == 1)
      read_thermodynamic_parameters();
  }

  /* We also need to print out errors here because the loop erases all
   *  errors at start */
  /* If there are fatal errors, write the proper message and exit */
  if (fatal_parse_err.data != NULL) {
    if (format_output) {
        format_error(stdout, sarg->sequence_name, fatal_parse_err.data);
    } else {
        print_boulder_error(fatal_parse_err.data);
    }
    fprintf(stderr, "%s: %s\n", 
              pr_program_name, fatal_parse_err.data);
    destroy_seq_args(sarg);
    exit(-4);
  }

  /* If there are nonfatal errors, write the proper message
   * and skip to the end of the loop */
  if (!pr_is_empty(&nonfatal_parse_err)) {
    if (format_output) {
        format_error(stdout, sarg->sequence_name, 
                     nonfatal_parse_err.data);
    } else {
        print_boulder_error(nonfatal_parse_err.data);
    }
  }

  /* The temporary sarg is not needed any more */
  destroy_seq_args(sarg);
  sarg = NULL;
  
  /* Read the data from input stream record by record and process it if
   * there are no errors. This is where the work is done. */
  while (1) {
    /* Create and initialize a seq_args data structure. sa (seq_args *) is 
     * initialized here because Values are _not_ retained across different
     * input records. */
    if (!(sarg = create_seq_arg())) {
      exit(-2);
    }

    /* Reset all errors handlers and the return structure */
    pr_set_empty(&fatal_parse_err);
    pr_set_empty(&nonfatal_parse_err);
    pr_set_empty(&warnings);
    retval = NULL;

    /* See read_boulder.h for documentation on read_boulder_record().*/
    if (!read_boulder_record(stdin, 
			     &strict_tags, 
			     &io_version,
			     !format_output, 
			     all_parameters,
			     global_pa, 
			     sarg, 
			     &fatal_parse_err, 
			     &nonfatal_parse_err,
			     &warnings,
			     &read_boulder_record_res)) {
      break; /* There were no more boulder records */
    }

    /* Check if the thermodynamical alignment flag was given and the path to the parameter files changed - we need to reread them */
    if ((global_pa->thermodynamic_alignment == 1) && (thermodynamic_path_changed == 1))
      read_thermodynamic_parameters();
    
    input_found = 1;
    if ((global_pa->primer_task == pick_detection_primers) 
                && (global_pa->pick_internal_oligo == 1)){
      PR_ASSERT(global_pa->pick_internal_oligo);
    }

    /* If there are fatal errors, write the proper message and exit */
    if (fatal_parse_err.data != NULL) {
      if (format_output) {
        format_error(stdout, sarg->sequence_name, fatal_parse_err.data);
      } else {
        print_boulder_error(fatal_parse_err.data);
      }
      fprintf(stderr, "%s: %s\n", 
              pr_program_name, fatal_parse_err.data);
      destroy_p3retval(retval);
      destroy_seq_args(sarg);
      exit(-4);
    }

    /* If there are nonfatal errors, write the proper message
     * and skip to the end of the loop */
    if (!pr_is_empty(&nonfatal_parse_err)) {
      if (format_output) {
        format_error(stdout, sarg->sequence_name, 
                     nonfatal_parse_err.data);
      } else {
        print_boulder_error(nonfatal_parse_err.data);
      }
      goto loop_wrap_up;
    }

    /* Print any warnings and continue processing */
    if (!pr_is_empty(&warnings)) {
      if (format_output) {
        format_warning(stdout, sarg->sequence_name, 
		       warnings.data);
      } else {
        print_boulder_warning(warnings.data);
      }
    }
    
    if (read_boulder_record_res.file_flag && sarg->sequence_name == NULL) {
      /* We will not have a base name for the files */
      if (format_output) {
        format_error(stdout, NULL, 
                     "Need PRIMER_SEQUENCE_ID if PRIMER_FILE_FLAG is not 0");
      } else {
        print_boulder_error("Need PRIMER_SEQUENCE_ID if PRIMER_FILE_FLAG is not 0");
      }
      goto loop_wrap_up;
    }

    /* Pick the primers - the central function */
    p3_set_gs_primer_file_flag(global_pa, 
                               read_boulder_record_res.file_flag);
    retval = choose_primers(global_pa, sarg);  
    if (NULL == retval) exit(-2); /* Out of memory. */
    
    /* This is old code to make it compatible with version 3 input.
       In future versions it can be deleted!
       If it was necessary to use a left_input, right_input,
       or internal_oligo_input primer that was
       unacceptable, then add warnings. */

    if (global_pa->pick_anyway && (io_version == 3
                || format_output)) {
      if (sarg->left_input) {
        add_must_use_warnings(&retval->warnings,
                              "Left primer", &retval->fwd.expl);
      }
      if (sarg->right_input) {
        add_must_use_warnings(&retval->warnings,
                              "Right primer", &retval->rev.expl);
      }
      if (sarg->internal_input) {
        add_must_use_warnings(&retval->warnings,
                              "Hybridization probe", &retval->intl.expl);
      }
    }
    /* End of the old code for compartibility. */

    if (pr_is_empty(&retval->glob_err)
        && pr_is_empty(&retval->per_sequence_err)) {
      /* We need to test for errors before we call
         p3_print_oligo_lists. This function only works on retval as
         returned when there were no errors. */
      if (read_boulder_record_res.file_flag) {
        /* Create files with left, right, and internal oligos. */
        p3_print_oligo_lists(retval, sarg, global_pa,
                             &retval->per_sequence_err,
                             sarg->sequence_name);
      }
    }

    if (format_output) {
      print_format_output(stdout, &io_version, global_pa, 
                          sarg, retval, pr_release,
                          read_boulder_record_res.explain_flag);
    } else {
      /* Use boulder output */
      print_boulder(/* & */io_version, global_pa, sarg, retval, 
                    read_boulder_record_res.explain_flag);
    }

  loop_wrap_up: /* Here the failed loops join in again */
    if (NULL != retval) {
      /* Check for errors and print them */
      if (NULL != retval->glob_err.data) {
        fprintf(stderr, "%s: %s\n", pr_program_name, retval->glob_err.data);
        destroy_p3retval(retval);
        destroy_seq_args(sarg);
        exit(-4);
      }
    }
    destroy_p3retval(retval); /* This works even if retval is NULL */
    retval = NULL;
    destroy_seq_args(sarg);
    sarg = NULL;

  }   /* while (1) (processing boulder io records) ...
         End of the primary working loop */

  /* To avoid being distracted when looking for leaks: */
  if (global_pa->thermodynamic_alignment == 1)
    destroy_thal_structures();
  p3_destroy_global_settings(global_pa);
  global_pa = NULL;
  destroy_seq_args(sarg);
  destroy_pr_append_str_data(&nonfatal_parse_err);
  destroy_pr_append_str_data(&fatal_parse_err);
  destroy_pr_append_str_data(&warnings);
  destroy_dpal_thal_arg_holder();
  if (thermodynamic_params_path)
    free(thermodynamic_params_path);
  /* If it could not read input complain and die */
  if (0 == input_found) {
    print_usage();
    exit(-3);
  }
  return 0;
}
Beispiel #2
0
int 
main (int argc, const char *argv[]) 
{
	unsigned int idx;
	char *end;
	int debug = 0;
	
	/* sequence file name specified by the user, otherwise STDIN will be used */
	const char *sequence_file_name = NULL;
	const char *lists_file_name = NULL;
	
	/* data structure for all k-mer lists used for masking */
	unsigned int nlists = 0;
	unsigned int nlist_parameters = 0;
	unsigned int npos = 0;
	unsigned int list_pos[MAX_VARIABLES], list_components[MAX_VARIABLES];
	
	masker_parameters mp = {};
	parameters_builder pbuilder = {};
	input_sequence *input_seq = NULL;
	
	pr_append_str parse_err;
	pr_append_str warnings;
	
	init_pr_append_str (&parse_err);
	init_pr_append_str (&warnings);

	/* fill mp with default parameters */
	mp.mdir = DEFAULT_MASKING_DIRECTION;
	mp.failure_rate = DEFAULT_FAILURE_RATE;
	mp.abs_cutoff = DEFAULT_ABS_CUTOFF;
	mp.nucl_masked_in_5p_direction = DEFAULT_M5P;
	mp.nucl_masked_in_3p_direction = DEFAULT_M3P;
	mp.print_sequence = PRINT_SEQUENCE;
	mp.do_soft_masking = HARD_MASKING;
	mp.masking_char = DEFAULT_MASK_CHAR;
	mp.list_prefix = DEFAULT_LIST_FILE_PREFIX;
	
	
	/* parsing and checking the commandline arguments */
	for (idx = 1; (int)idx < argc; idx++) {
		
  		if (!strcmp (argv[idx], "-h") || !strcmp (argv[idx], "--help") || !strcmp (argv[idx], "-?")) {
			print_help (0);
		
		} else if ((int)idx == argc - 1 && argv[idx][0] != '-') {
			sequence_file_name = argv[idx];
			
		} else if (!strcmp (argv[idx], "-lf") || !strcmp (argv[idx], "--lists_file")) {
			/* lists specified from the file */
			if (!argv[idx + 1] || argv[idx + 1][0] == '-') {
				pr_append_new_chunk_external (&warnings, "No lists file specified.");
				idx += 1;
				continue;
			}
			lists_file_name = argv[idx + 1];
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-l") || !strcmp (argv[idx], "--list")) {
			/* lists specified from the commandline */
			if (nlist_parameters == MAX_VARIABLES) {
				pr_append_new_chunk_external (&parse_err, "Maximum number of list variables reached.");
				break;
			}
			
			if (!argv[idx + 1]) {
				pr_append_new_chunk_external (&warnings, "No list name specified with -l parameter!.");
				continue;
			}
			
			/* get the positions of list files */
			list_pos[nlist_parameters] = idx;
			while (argv[idx + 1]) {
				if (argv[idx + 1][0] == '-') {
					if (!argv[idx + 1][1]) break;
					strtod (argv[idx + 1] + 1, &end);
					if (*end != 0) break;
				} else if (idx + 1 != list_pos[nlist_parameters] + 1 && idx + 1 != list_pos[nlist_parameters] + 4) {
					strtod (argv[idx + 1], &end);
					if (*end != 0) break;
				} else if (idx + 1 == list_pos[nlist_parameters] + 4 && strcmp (argv[idx + 1], "sq")) {
					break;
				}
				idx += 1;
			}
			list_components[nlist_parameters] = idx - list_pos[nlist_parameters];
			nlist_parameters += 1;
			npos += 1;
			
		} else if (!strcmp (argv[idx], "-lp") || !strcmp (argv[idx], "--list_prefix")) {
			/* lists specified by the prefix */
			if (!argv[idx + 1] || argv[idx + 1][0] == '-') {
				pr_append_new_chunk_external (&warnings, "No list prefix specified! Using the default value.");
				idx += 1;
				continue;
			}
			mp.list_prefix = (char *)argv[idx + 1];
			idx += 1;			
			
		} else if (!strcmp (argv[idx], "-p") || !strcmp (argv[idx], "--probability_cutoff")) {
			if (!argv[idx + 1]) {
				pr_append_new_chunk_external (&warnings, "No cutoff value specified! Using the default value.");
				idx += 1;
				continue;
			}
			mp.failure_rate = strtod (argv[idx + 1], &end);
			mp.abs_cutoff = 0;
			if (*end != 0 || mp.failure_rate < 0 || mp.failure_rate > 1) {
				pr_append_new_chunk_external (&parse_err, "Invalid cutoff value: ");
				pr_append_external (&parse_err, argv[idx + 1]);
				break;
			}
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-a") || !strcmp (argv[idx], "--absolute_value_cutoff")) {
			if (!argv[idx + 1]) {
				pr_append_new_chunk_external (&warnings, "No absolute cutoff value specified! Using the default value.");
				idx += 1;
				continue;
			}
			mp.abs_cutoff = strtod (argv[idx + 1], &end);
			mp.failure_rate = 0.0;
			if (*end != 0 || mp.abs_cutoff < 0) {
				pr_append_new_chunk_external (&parse_err, "Invalid absolute cutoff value: ");
				pr_append_external (&parse_err, argv[idx + 1]);
				break;
			}
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-m5") || !strcmp (argv[idx], "--mask_5p")) {
			if (!argv[idx + 1]) {
				pr_append_new_chunk_external (&warnings, "Number of nucleotides masked in 5' direction not specified! Using the default value.");
				idx += 1;
				continue;
			}
			mp.nucl_masked_in_5p_direction = strtod (argv[idx + 1], &end);
			if (*end != 0) {
				pr_append_new_chunk_external (&parse_err, "Invalid number of nucleotides masked in 5' direction: ");
				pr_append_external (&parse_err, argv[idx + 1]);
				break;
			}
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-m3") || !strcmp (argv[idx], "--mask_3p")) {
			if (!argv[idx + 1]) {
				pr_append_new_chunk_external (&warnings, "Number of nucleotides masked in 3' direction not specified! Using the default value.");
				idx += 1;
				continue;
			}
			mp.nucl_masked_in_3p_direction = strtod (argv[idx + 1], &end);
			if (*end != 0) {
				pr_append_new_chunk_external (&parse_err, "Invalid number of nucleotides masked in 3' direction: ");
				pr_append_external (&parse_err, argv[idx + 1]);
				break;
			}
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-c") || !strcmp (argv[idx], "--masking_char")) {
			if (!argv[idx + 1 || argv[idx + 1][0] == '-']) {
				pr_append_new_chunk_external (&warnings, "Character for masking not specified! Using the default value.");
				idx += 1;
				continue;
			}
			mp.masking_char = argv[idx + 1][0];
			if (strlen(argv[idx + 1]) > 1 || mp.masking_char < 33 || mp.masking_char > 126) {
				pr_append_new_chunk_external (&parse_err, "Invalid character for masking: ");
				pr_append_external (&parse_err, argv[idx + 1]);
				break;
			}
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-d") || !strcmp (argv[idx], "--masking_direction")) {
			if (!argv[idx + 1] || argv[idx + 1][0] == '-') {
				pr_append_new_chunk_external (&warnings, "Masking direction not specified! Masking both strands by default.");
			} else if (!strcmp (argv[idx + 1], "both")) {
				mp.mdir = both_on_same;
			} else if (!strcmp (argv[idx + 1], "fwd")) {
				mp.mdir = fwd;
			} else if (!strcmp (argv[idx + 1], "rev")) {
				mp.mdir = rev;
			} else {
				pr_append_new_chunk_external (&warnings, "Unknown masking direction: ");
				pr_append_external (&warnings, argv[idx + 1]);
				pr_append_external (&warnings, ". Masking both strands by default.");
			}
			idx += 1;
			
		} else if (!strcmp (argv[idx], "-s") || !strcmp (argv[idx], "--soft_mask")) {
			mp.do_soft_masking = SOFT_MASKING;

		} else if (!strcmp (argv[idx], "-D")) {
			debug += 1;
			
		} else {
			pr_append_new_chunk_external (&parse_err, "Unknown parameter: ");
			pr_append_external (&parse_err, argv[idx]);
			break;
		}
	}

	input_seq = create_input_sequence_from_file_name (sequence_file_name, &parse_err);

	if (parse_err.data != NULL) {
		fprintf(stderr, "%s -> parsing commandline: ERROR: %s\n", pr_programme_name, parse_err.data);
		exit(-1);
	}
	if (warnings.data != NULL) {
		fprintf(stderr, "%s -> parsing commandline: WARNING: %s\n", pr_programme_name, warnings.data);
	}
	
	if (lists_file_name) {
		/* if lists are given in a text file */
		mp.fp = read_formula_parameters_from_file (lists_file_name, &nlist_parameters, &pbuilder, &mp.formula_intercept, &parse_err);
		nlists = pbuilder.nfp;
	} 
	
	if (npos != 0) {
		/* if lists are given by commandline arguments (can be added to the ones given in a file) */
		pbuilder.fp_array = mp.fp;

		for (idx = 0; idx < npos; idx++) {
			unsigned int pos = list_pos[idx] + 1;
			char *values[4];
			unsigned int nvalues = list_components[idx];
			char *end;
			memcpy (&values, &argv[pos], nvalues * sizeof(*argv));
			
			if (nvalues == 1) {
				double ic;
				double neg = 1.0;
				if (values[0][0] == '-') {
					values[0] += 1;
					neg = -1.0;
				}
				ic = strtod (values[0], &end);
				if (*end == 0) {
					mp.formula_intercept = ic * neg;
					continue;
				}
			}
			add_variable_to_formula_parameters (values, nvalues, &pbuilder, &parse_err);
		}		
		nlists = pbuilder.nfp;
		mp.fp = pbuilder.fp_array;

	} else if (nlists == 0 && !lists_file_name) {
		/* if there are no lists specified use the default formula */
		mp.fp = create_default_formula_parameters (mp.list_prefix, &parse_err);
		mp.formula_intercept = DEFAULT_INTERCEPT;
		nlists = DEFAULT_NLISTS;
		nlist_parameters = DEFAULT_NLIST_PARAMETERS;
	} 

	mp.nlists = nlists;
	
	if (mp.abs_cutoff > 0 && nlist_parameters != 1) {
		fprintf (stderr, "Error: Absolute value cutoff works with one list and one k-mer frequency parameter only. Currently you are using %u lists and %u parameters.\n", nlists, nlist_parameters);
		print_help(1);
	}

	if (parse_err.data != NULL) {
		fprintf(stderr, "%s -> building formula: ERROR: %s\n", pr_programme_name, parse_err.data);
		delete_input_sequence (input_seq);
		exit(-1);
	}
	
	if (debug > 0) print_parameters (&mp);
	
	read_and_mask_sequence (input_seq, NULL, &mp, &parse_err, debug);

	if (parse_err.data != NULL) {
		fprintf(stderr, "%s -> masking sequence: ERROR: %s\n", pr_programme_name, parse_err.data);
		delete_input_sequence (input_seq);
		delete_formula_parameters (mp.fp, nlists);
		exit(-1);
	}
	
	destroy_pr_append_str_data (&warnings);
	destroy_pr_append_str_data (&parse_err);
	delete_input_sequence (input_seq);
	delete_formula_parameters (mp.fp, nlists);
	
	if (debug > 0) fprintf (stderr, "Done!\n");
	return 0;
}
int
main(int argc, char *argv[]) { 
  /* Setup the input data structures handlers */
  int format_output = 0;
  int strict_tags = 0;
  int dump_args = 0 ; /* set to 1 if dumping arguments to choose_primers */
  int io_version = 4;

  /* Some space for file names */
  char *tmp_file_name = NULL;
  char p3_all_file[FILE_NAME_SIZE];
  char p3_settings_file[FILE_NAME_SIZE];

  p3_global_settings *global_pa;
  seq_args *sarg;
  read_boulder_record_results read_boulder_record_res = {0,0};

  pr_append_str fatal_parse_err;
  pr_append_str nonfatal_parse_err;
  
  /* Retval will point to the return value from choose_primers(). */
  p3retval *retval = NULL;
  int input_found=0;

  p3_all_file[0] = '\0';
  p3_settings_file[0] = '\0';

  init_pr_append_str(&fatal_parse_err);
  init_pr_append_str(&nonfatal_parse_err);


  /* Get the program name for correct error messages */
  pr_program_name = argv[0];
  p3_set_program_name(pr_program_name);

  /* We set up some signal handlers in case someone starts up the program
   * from the command line, wonders why nothing is happening, and then kills
   * the program. */
  signal(SIGINT, sig_handler);
  signal(SIGTERM, sig_handler);

  /* Allocate the space for global settings and fill in default parameters */
  global_pa = p3_create_global_settings();
  if (!global_pa) {
    exit(-2); /* Out of memory. */
  }
  if (dump_args) global_pa->dump = 1 ;
  
  /* Read in the flags provided with the program call */
  while (--argc > 0) {
    argv++;
    if (!strcmp(*argv, "-format_output")) {
      format_output = 1;
    } else if (!strcmp(*argv, "-about")) {
          printf( "%s\n", pr_release);
          exit (0);
    } else if (!strcmp(*argv, "-2x_compat")) {
          printf( "PRIMER_ERROR=flag -2x_compat is no longer supported\n=\n");
          exit (-1);
    } else if (!strcmp(*argv, "-io_version=3")) {
          io_version = 3;
    } else if (!strcmp(*argv, "-io_version=4")) {
          io_version = 4;
    } else if (!strncmp(*argv, "-p3_settings_file=", 18)) {
      tmp_file_name = strchr(*argv,'=') + 1;
      strncpy (p3_settings_file,tmp_file_name,FILE_NAME_SIZE-1);
    } else if (!strcmp(*argv, "-strict_tags")) {
      strict_tags = 1;
    } else  {
      print_usage();
      exit(-1);
    }
  }

  /* Settings files have to be read in just below, and
     the functions need a temporary sarg */
  if (!(sarg = create_seq_arg())) {
    exit(-2);
  }

  /* Read data from the settings file until a "=" line occurs.  Assign parameter
   * values for primer picking to pa and sa. */
  if (p3_settings_file[0] != '\0') {
    read_p3_file(p3_settings_file, settings, global_pa, 
                 sarg, &fatal_parse_err, &nonfatal_parse_err,
                 &read_boulder_record_res);
  }

  /* We also need to print out errors here because the loop erases all
   *  errors at start */
  /* If there are fatal errors, write the proper message and exit */
  if (fatal_parse_err.data != NULL) {
    if (format_output) {
        format_error(stdout, sarg->sequence_name, fatal_parse_err.data);
    } else {
        print_boulder_error(fatal_parse_err.data);
    }
    fprintf(stderr, "%s: %s\n", 
              pr_program_name, fatal_parse_err.data);
    destroy_seq_args(sarg);
    exit(-4);
  }

  /* If there are nonfatal errors, write the proper message
   * and skip to the end of the loop */
  if (!pr_is_empty(&nonfatal_parse_err)) {
    if (format_output) {
        format_error(stdout, sarg->sequence_name, 
                     nonfatal_parse_err.data);
    } else {
        print_boulder_error(nonfatal_parse_err.data);
    }
  }

  /* The temporary sarg is not needed any more */
  destroy_seq_args(sarg);
  sarg = NULL;
  
  /* Read the data from input stream record by record and process it if
   * there are no errors. This is where the work is done. */
  while (1) {
    /* Create and initialize a seq_args data structure. sa (seq_args *) is 
     * initialized here because Values are _not_ retained across different
     * input records. */
    if (!(sarg = create_seq_arg())) {
      exit(-2);
    }

    /* Reset all errors handlers and the return structure */
    pr_set_empty(&fatal_parse_err);
    pr_set_empty(&nonfatal_parse_err);
    retval = NULL;

    /* See read_boulder.h for documentation on read_boulder_record().*/
    if (!read_boulder_record(stdin, 
                            &strict_tags, 
                            &io_version,
                            !format_output, 
                            all_parameters,
                            global_pa, 
                            sarg, 
                            &fatal_parse_err, 
                            &nonfatal_parse_err,
                            &read_boulder_record_res)) {
      break; /* There were no more boulder records */
    }
    
    input_found = 1;
    if ((global_pa->primer_task == pick_detection_primers) 
                && (global_pa->pick_internal_oligo == 1)){
      PR_ASSERT(global_pa->pick_internal_oligo);
    }

    /* If there are fatal errors, write the proper message and exit */
    if (fatal_parse_err.data != NULL) {
      if (format_output) {
        format_error(stdout, sarg->sequence_name, fatal_parse_err.data);
      } else {
        print_boulder_error(fatal_parse_err.data);
      }
      fprintf(stderr, "%s: %s\n", 
              pr_program_name, fatal_parse_err.data);
      destroy_p3retval(retval);
      destroy_seq_args(sarg);
      exit(-4);
    }

    /* If there are nonfatal errors, write the proper message
     * and skip to the end of the loop */
    if (!pr_is_empty(&nonfatal_parse_err)) {
      if (format_output) {
        format_error(stdout, sarg->sequence_name, 
                     nonfatal_parse_err.data);
      } else {
        print_boulder_error(nonfatal_parse_err.data);
      }
      goto loop_wrap_up;
    }
    
    if (read_boulder_record_res.file_flag && sarg->sequence_name == NULL) {
      /* We will not have a base name for the files */
      if (format_output) {
        format_error(stdout, NULL, 
                     "Need PRIMER_SEQUENCE_ID if PRIMER_FILE_FLAG is not 0");
      } else {
        print_boulder_error("Need PRIMER_SEQUENCE_ID if PRIMER_FILE_FLAG is not 0");
      }
      goto loop_wrap_up;
    }

    /* Pick the primers - the central function */
    p3_set_gs_primer_file_flag(global_pa, 
                               read_boulder_record_res.file_flag);
    retval = choose_primers(global_pa, sarg);  
    if (NULL == retval) exit(-2); /* Out of memory. */
    
    /* This is old code to make it compatible with version 3 input.
       In future versions it can be deleted!
       If it was necessary to use a left_input, right_input,
       or internal_oligo_input primer that was
       unacceptable, then add warnings. */

    if (global_pa->pick_anyway && (io_version == 3
                || format_output)) {
      if (sarg->left_input) {
        add_must_use_warnings(&retval->warnings,
                              "Left primer", &retval->fwd.expl);
      }
      if (sarg->right_input) {
        add_must_use_warnings(&retval->warnings,
                              "Right primer", &retval->rev.expl);
      }
      if (sarg->internal_input) {
        add_must_use_warnings(&retval->warnings,
                              "Hybridization probe", &retval->intl.expl);
      }
    }
    /* End of the old code for compartibility. */

    if (pr_is_empty(&retval->glob_err)
        && pr_is_empty(&retval->per_sequence_err)) {
      /* We need to test for errors before we call
         p3_print_oligo_lists. This function only works on retval as
         returned when there were no errors. */
      if (read_boulder_record_res.file_flag) {
        /* Create files with left, right, and internal oligos. */
        p3_print_oligo_lists(retval, sarg, global_pa,
                             &retval->per_sequence_err,
                             sarg->sequence_name);
      }
    }

    if (format_output) {
      print_format_output(stdout, &io_version, global_pa, 
                          sarg, retval, pr_release,
                          read_boulder_record_res.explain_flag);
    } else {
      /* Use boulder output */
      print_boulder(/* & */io_version, global_pa, sarg, retval, 
                    read_boulder_record_res.explain_flag);
    }

  loop_wrap_up: /* Here the failed loops join in again */
    if (NULL != retval) {
      /* Check for errors and print them */
      if (NULL != retval->glob_err.data) {
        fprintf(stderr, "%s: %s\n", pr_program_name, retval->glob_err.data);
        destroy_p3retval(retval);
        destroy_seq_args(sarg);
        exit(-4);
      }
    }
    destroy_p3retval(retval); /* This works even if retval is NULL */
    retval = NULL;
    destroy_seq_args(sarg);
    sarg = NULL;

  }   /* while (1) (processing boulder io records) ...
         End of the primary working loop */

  /* To avoid being distracted when looking for leaks: */
  p3_destroy_global_settings(global_pa);
  global_pa = NULL;
  destroy_seq_args(sarg);
  destroy_pr_append_str_data(&nonfatal_parse_err);
  destroy_pr_append_str_data(&fatal_parse_err);
  destroy_dpal_thal_arg_holder();
  /* If it could not read input complain and die */
  if (0 == input_found) {
    print_usage();
    exit(-3);
  }
  return 0;
}