static void pr_append(pr_append_str *x, const char *s) { if (pr_append_external(x, s)) out_of_memory_error(); }
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; }
static void p3sl_append(pr_append_str *x, const char *s) { int r = pr_append_external(x, s); if (r) longjmp(_jmp_buf, 1); }
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 io_version = 4; int default_version = 2; int dump_args = 0; p3_global_settings *global_pa; seq_args *sarg; read_boulder_record_results read_boulder_record_res = {0,0}; pr_append_str p3_settings_path; pr_append_str output_path; pr_append_str error_path; 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'}, {"default_version", required_argument, 0, 'd'}, {"Dump_args", no_argument, 0, 'D'}, {"2x_compat", no_argument, 0, '2'}, {"output", required_argument, 0, 'o'}, {"error", required_argument, 0, 'e'}, {0, 0, 0, 0} }; int about = 0, compat = 0, invalid_flag = 0; /* Retval will point to the return value from choose_primers(). */ p3retval *retval = NULL; int input_found=0; init_pr_append_str(&fatal_parse_err); init_pr_append_str(&nonfatal_parse_err); init_pr_append_str(&warnings); init_pr_append_str(&p3_settings_path); init_pr_append_str(&output_path); init_pr_append_str(&error_path); /* 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); /* 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': if (pr_append_external(&p3_settings_path, optarg)) { exit(-2); /* Out of memory. */ } break; case 'i': if (!strcmp(optarg, "4")) io_version = 4; else io_version = -1; break; case 'd': /* default_version */ if (!strcmp(optarg, "1")) default_version = 1; else if (!strcmp(optarg, "2")) default_version = 2; else default_version = -1; break; case 'D': /* Undocumented flag for testing; causes values of arguments to be echoed to stdout. */ dump_args = 1; break; case '2': compat = 1; break; case 'o': if (pr_append_external(&output_path, optarg)) { exit(-2); /* Out of memory. */ } break; case 'e': if (pr_append_external(&error_path, optarg)) { exit(-2); /* Out of memory. */ } break; case '?': invalid_flag = 1; break; } } /* Open the output and error files specified */ if (!pr_is_empty(&error_path)) { /* reassign stderr */ if (freopen(pr_append_str_chars(&error_path), "w", stderr) == NULL) { fprintf(stderr, "Error creating file %s\n", pr_append_str_chars(&error_path)); exit(-1); } destroy_pr_append_str_data(&error_path); } if (!pr_is_empty(&output_path)) { /* reassign stdout */ if (freopen(pr_append_str_chars(&output_path), "w", stdout) == NULL) { fprintf(stderr, "Error creating file %s\n", pr_append_str_chars(&output_path)); exit(-1); } destroy_pr_append_str_data(&output_path); } /* 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) || (default_version == -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 on the command line. */ 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); } } /* Allocate the space for global settings and fill in default parameters */ if (default_version == 1) global_pa = p3_create_global_settings_default_version_1(); else if (default_version == 2) global_pa = p3_create_global_settings(); else { print_usage(); exit(-1); } if (!global_pa) { exit(-2); /* Out of memory. */ } global_pa->dump = dump_args ; /* 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 (!pr_is_empty(&p3_settings_path)) { read_p3_file(pr_append_str_chars(&p3_settings_path), settings, echo_settings && !format_output, strict_tags, global_pa, sarg, &fatal_parse_err, &nonfatal_parse_err, &warnings, &read_boulder_record_res); destroy_pr_append_str_data(&p3_settings_path); /* Check if any thermodynamical alignment flag was given */ if ((global_pa->thermodynamic_oligo_alignment == 1) || (global_pa->thermodynamic_template_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 any thermodynamical alignment flag was given and the path to the parameter files changed - we need to reread them */ if (((global_pa->thermodynamic_oligo_alignment == 1) || (global_pa->thermodynamic_template_alignment == 1)) && (thermodynamic_path_changed == 1)) read_thermodynamic_parameters(); /* Check that we found the thermodynamic parameters in case any thermodynamic flag was set to 1. */ if (((global_pa->thermodynamic_oligo_alignment == 1) || (global_pa->thermodynamic_template_alignment == 1)) && (thermodynamic_params_path == NULL)) { /* no parameter directory found, error */ printf("PRIMER_ERROR=thermodynamic approach chosen, but path to thermodynamic parameters not specified\n=\n"); exit(-1); } input_found = 1; if ((global_pa->primer_task == generic) && (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. */ /* 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 && 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); } } 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_oligo_alignment == 1) || (global_pa->thermodynamic_template_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(); free(thermodynamic_params_path); /* If it could not read input, then complain and die */ if (0 == input_found) { print_usage(); exit(-3); } return 0; }