/* Copyright (c) 1996,1997,1998,1999,2000,2001,2004,2006,2007,2008,2009, 2010,2011,2012 Whitehead Institute for Biomedical Research, Steve Rozen (http://purl.com/STEVEROZEN/), Andreas Untergasser and Helen Skaletsky. All rights reserved. This file is part of primer3 and the primer3 suite. Primer3 and the primer3 suite are free software; you can redistribute them and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This software 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this file (file gpl-2.0.txt in the source distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include /* strtod, strtol,... */ #include /* toupper, isspace */ #include /* memset, strlen, strcmp, ... */ #include #include "read_boulder.h" #include "masker.h" #define INIT_BUF_SIZE 1024 #define INIT_LIB_SIZE 500 #define PR_MAX_LIBRARY_WT 100.0 char *thermodynamic_params_path = NULL; int thermodynamic_path_changed = 1; char *kmer_lists_path = NULL; /* Static functions. */ static void *_rb_safe_malloc(size_t x); static void out_of_memory_error(); static void parse_double(const char *, const char *, double *, pr_append_str *); static void parse_int(const char *, const char *, int *, pr_append_str *); static const char *parse_int_pair(const char *, const char *, char, int *, int *, pr_append_str *); static char *parse_2_int_pair(const char*, char*, char, char, int*, int*, int*, int*, pr_append_str*); static void parse_interval_list(const char *tag_name, const char *datum, interval_array_t2 *interval_arr, pr_append_str *err); static void parse_2_interval_list(const char *tag_name, char *datum, interval_array_t4 *interval_arr, pr_append_str *err); static int parse_intron_list(char *, int *, int *); static void parse_product_size(const char *, char *, p3_global_settings *, pr_append_str *); static void pr_append(pr_append_str *, const char *); static void pr_append_new_chunk(pr_append_str *x, const char *s); static void tag_syntax_error(const char *, const char *, pr_append_str *); static int parse_seq_quality(char *, seq_args *); static const char *pr_program_name = "TMP"; /* * Hack to support old SunOS headers. (We do not try to declare _all_ * undeclared functions; only those with non-int return types.) */ #ifndef __cplusplus extern double strtod(); #endif /* * See read_boulder.h for description. */ #define COMPARE(TAG) (!strncmp(s, TAG, tag_len) \ && ('=' == s[tag_len] || ' ' == s[tag_len]) \ && '\0' == TAG[tag_len]) #define COMPARE_AND_MALLOC(TAG,T) \ if (COMPARE(TAG)) { \ if (T) { \ pr_append_new_chunk(parse_err, \ "Duplicate tag: "); \ pr_append(parse_err, TAG); \ } else { \ T = (char*) _rb_safe_malloc(datum_len + 1); \ strcpy(T, datum); \ } \ continue; \ } #define OVERWRITE_COMPARE_AND_MALLOC(TAG,T) \ if (COMPARE(TAG)) { \ if (T) { \ free(T); \ } \ T = (char*) _rb_safe_malloc(datum_len + 1); \ strcpy(T, datum); \ continue; \ } #define COMPARE_FLOAT(TAG,T) \ if (COMPARE(TAG)) { \ parse_double(TAG, datum, &(T), parse_err); \ continue; \ } /* This macro added 2011 08 28 to allow use of of the 'setting' functions, such as p3_set_gs_primer_self_end, in setting arguments. At this point, this macro is only used twice, and there are no analogous macros for integers,etc. */ #define COMPARE_FLOAT_USE_FN(TAG, set_function) \ if (COMPARE(TAG)) { \ parse_double(TAG, datum, &tmp_double, parse_err); \ set_function(pa, tmp_double); \ continue; \ } #define COMPARE_INT(TAG,T) \ if (COMPARE(TAG)) { \ parse_int(TAG, datum, &(T), parse_err); \ continue; \ } #define COMPARE_INTERVAL_LIST(TAG, PLACE) \ if (COMPARE(TAG)) { \ parse_interval_list(TAG, datum, PLACE, parse_err); \ continue; \ } #define COMPARE_2_INTERVAL_LIST(TAG, PLACE) \ if (COMPARE(TAG)) { \ parse_2_interval_list(TAG, datum, PLACE, parse_err);\ continue; \ } /* c-basic-offset in emacs is set to 2 */ /* * See read_boulder.h for description. */ int read_boulder_record(FILE *file_input, const int *strict_tags, const int *io_version, int echo_output, /* should be echo_input */ const p3_file_type file_type, p3_global_settings *pa, seq_args *sa, pr_append_str *glob_err, /* Really should be called fatal_parse_err */ pr_append_str *nonfatal_parse_err, pr_append_str *warnings, read_boulder_record_results *res) { int line_len; int tag_len, datum_len; int data_found = 0; char *s, *n, *datum, *task_tmp = NULL; const char *p; pr_append_str *parse_err; pr_append_str *non_fatal_err; char *repeat_file_path = NULL, *int_repeat_file_path = NULL; int tmp_int; /* int min_3_prime = 0, min_5_prime = 0; Removed 10/20/2010 */ int min_3_prime_distance_global = 0; /* needed to check if both global and specific*/ int min_3_prime_distance_specific = 0; /* are given in same boulder record */ int min_three_prime_distance; /* holder for the value of this tag */ double tmp_double; non_fatal_err = nonfatal_parse_err; while (((s = p3_read_line(file_input)) != NULL) && (strcmp(s,"="))) { /* If we are reading from a settings file, then skip every line except those begining "PRIMER_" or "P3_FILE_ID". Hint: strncomp returns 0 if both strings are equal */ if (file_type == settings && strncmp(s, "PRIMER_", 7) /* "s does not begin with 'PRIMER_'" */ && strncmp(s, "P3_FILE_ID", 10) /* "s does not begin with 'P3_FILE_ID'" */ ) { continue; } /* Silently ignore all primer3plus tags */ /* Removed 10/20/10 because P3P_ tags are already ignored in settings files. */ /* if (!(strncmp(s, "P3P_", 4))) { continue; } */ data_found = 1; /* Print out the input */ if (echo_output) printf("%s\n", s); line_len = strlen(s); /* If the line has an "=" read the tag in the right place */ if ((n=strchr(s,'=')) == NULL) { /* The input line is illegal because it has no * "=" in it, but we still will read to the end * of the record. */ pr_append_new_chunk(glob_err, "Input line with no '=': "); pr_append(glob_err, s); continue; } /* Read in the new tags used from primer3 version 2.0 on */ else { /* Get the tag and the value pointers */ tag_len = n - s; datum = n + 1; datum_len = line_len - tag_len - 1; /* Process "Sequence" (i.e. Per-Record) Arguments". */ parse_err = non_fatal_err; /* COMPARE_AND_MALLOC("SEQUENCE", sa->sequence); */ if (COMPARE("SEQUENCE_TEMPLATE")) { /* NEW WAY */ if (/* p3_get_seq_arg_sequence(sa) */ sa->sequence) { pr_append_new_chunk(parse_err, "Duplicate tag: "); pr_append(parse_err, "SEQUENCE_TEMPLATE"); } else { if (p3_set_sa_sequence(sa, datum)) exit(-2); } continue; } if (COMPARE("SEQUENCE_QUALITY")) { if ((sa->n_quality = parse_seq_quality(datum, sa)) == 0) { pr_append_new_chunk(parse_err, "Error in sequence quality data"); } continue; } COMPARE_AND_MALLOC("SEQUENCE_ID", sa->sequence_name); COMPARE_AND_MALLOC("SEQUENCE_PRIMER", sa->left_input); COMPARE_AND_MALLOC("SEQUENCE_PRIMER_REVCOMP", sa->right_input); COMPARE_AND_MALLOC("SEQUENCE_INTERNAL_OLIGO", sa->internal_input); COMPARE_2_INTERVAL_LIST("SEQUENCE_PRIMER_PAIR_OK_REGION_LIST", &sa->ok_regions); COMPARE_INTERVAL_LIST("SEQUENCE_TARGET", &sa->tar2); COMPARE_INTERVAL_LIST("SEQUENCE_EXCLUDED_REGION", &sa->excl2); COMPARE_INTERVAL_LIST("SEQUENCE_INTERNAL_EXCLUDED_REGION", &sa->excl_internal2); if (COMPARE("SEQUENCE_OVERLAP_JUNCTION_LIST")) { if (parse_intron_list(datum, sa->primer_overlap_junctions, &sa->primer_overlap_junctions_count) == 0) { pr_append_new_chunk(parse_err, "Error in SEQUENCE_PRIMER_OVERLAP_JUNCTION_LIST"); } continue; } if (COMPARE("SEQUENCE_INCLUDED_REGION")) { p = parse_int_pair("SEQUENCE_INCLUDED_REGION", datum, ',', &sa->incl_s, &sa->incl_l, parse_err); if (NULL == p) /* An error; the message is already * in parse_err. */ continue; while (' ' == *p || '\t' == *p) p++; if (*p != '\n' && *p != '\0') tag_syntax_error("SEQUENCE_INCLUDED_REGION", datum, parse_err); continue; } COMPARE_INT("SEQUENCE_START_CODON_POSITION", sa->start_codon_pos); COMPARE_INT("SEQUENCE_FORCE_LEFT_START", sa->force_left_start); COMPARE_INT("SEQUENCE_FORCE_LEFT_END", sa->force_left_end); COMPARE_INT("SEQUENCE_FORCE_RIGHT_START", sa->force_right_start); COMPARE_INT("SEQUENCE_FORCE_RIGHT_END", sa->force_right_end); /* Process "Global" Arguments (those that persist between boulder * records). */ parse_err = glob_err; /* These errors are considered fatal. */ if (COMPARE("PRIMER_PRODUCT_SIZE_RANGE")) { parse_product_size("PRIMER_PRODUCT_SIZE_RANGE", datum, pa, parse_err); continue; } COMPARE_INT("PRIMER_OPT_SIZE", pa->p_args.opt_size); COMPARE_INT("PRIMER_MIN_SIZE", pa->p_args.min_size); COMPARE_INT("PRIMER_MAX_SIZE", pa->p_args.max_size); COMPARE_INT("PRIMER_MAX_POLY_X", pa->p_args.max_poly_x); COMPARE_FLOAT("PRIMER_OPT_TM", pa->p_args.opt_tm); COMPARE_FLOAT("PRIMER_OPT_GC_PERCENT", pa->p_args.opt_gc_content); COMPARE_FLOAT("PRIMER_MIN_TM", pa->p_args.min_tm); COMPARE_FLOAT("PRIMER_MAX_TM", pa->p_args.max_tm); COMPARE_FLOAT("PRIMER_PAIR_MAX_DIFF_TM", pa->max_diff_tm); if (COMPARE("PRIMER_TM_FORMULA")) { parse_int("PRIMER_TM_FORMULA", datum, &tmp_int, parse_err); pa->tm_santalucia = (tm_method_type) tmp_int; /* added by T.Koressaar */ continue; } if (COMPARE("PRIMER_SALT_CORRECTIONS")) { parse_int("PRIMER_SALT_CORRECTIONS", datum, &tmp_int, parse_err); pa->salt_corrections = (salt_correction_type) tmp_int; /* added by T.Koressaar */ continue; } COMPARE_FLOAT("PRIMER_MIN_GC", pa->p_args.min_gc); COMPARE_FLOAT("PRIMER_MAX_GC", pa->p_args.max_gc); COMPARE_FLOAT("PRIMER_SALT_MONOVALENT", pa->p_args.salt_conc); COMPARE_FLOAT("PRIMER_SALT_DIVALENT", pa->p_args.divalent_conc); COMPARE_FLOAT("PRIMER_DNTP_CONC", pa->p_args.dntp_conc); COMPARE_FLOAT("PRIMER_DNA_CONC", pa->p_args.dna_conc); COMPARE_INT("PRIMER_MAX_NS_ACCEPTED", pa->p_args.num_ns_accepted); COMPARE_INT("PRIMER_PRODUCT_OPT_SIZE", pa->product_opt_size); COMPARE_FLOAT("PRIMER_MAX_SELF_ANY", pa->p_args.max_self_any); /* COMPARE_FLOAT("PRIMER_MAX_SELF_END", pa->p_args.max_self_end); */ /* NEW */ COMPARE_FLOAT_USE_FN("PRIMER_MAX_SELF_END", p3_set_gs_primer_self_end) /* if (COMPARE("PRIMER_MAX_SELF_END")) { parse_double("PRIMER_MAX_SELF_END", datum, &tmp_double, parse_err); p3_set_gs_primer_self_end(pa, tmp_double); continue; } */ COMPARE_FLOAT("PRIMER_MAX_SELF_ANY_TH", pa->p_args.max_self_any_th); COMPARE_FLOAT("PRIMER_MAX_SELF_END_TH", pa->p_args.max_self_end_th); COMPARE_FLOAT("PRIMER_MAX_HAIRPIN_TH", pa->p_args.max_hairpin_th); COMPARE_FLOAT("PRIMER_PAIR_MAX_COMPL_ANY", pa->pair_compl_any); COMPARE_FLOAT("PRIMER_PAIR_MAX_COMPL_END", pa->pair_compl_end); COMPARE_FLOAT("PRIMER_PAIR_MAX_COMPL_ANY_TH", pa->pair_compl_any_th); COMPARE_FLOAT("PRIMER_PAIR_MAX_COMPL_END_TH", pa->pair_compl_end_th); COMPARE_INT("P3_FILE_FLAG", res->file_flag); COMPARE_INT("PRIMER_PICK_ANYWAY", pa->pick_anyway); COMPARE_INT("PRIMER_GC_CLAMP", pa->gc_clamp); COMPARE_INT("PRIMER_MAX_END_GC", pa->max_end_gc); COMPARE_INT("PRIMER_EXPLAIN_FLAG", res->explain_flag); COMPARE_INT("PRIMER_LIBERAL_BASE", pa->liberal_base); COMPARE_INT("PRIMER_FIRST_BASE_INDEX", pa->first_base_index); COMPARE_INT("PRIMER_NUM_RETURN", pa->num_return); COMPARE_INT("PRIMER_MIN_QUALITY", pa->p_args.min_quality); COMPARE_INT("PRIMER_MIN_END_QUALITY", pa->p_args.min_end_quality); if (COMPARE("PRIMER_MIN_THREE_PRIME_DISTANCE")) { parse_int("PRIMER_MIN_THREE_PRIME_DISTANCE", datum, &(min_three_prime_distance), parse_err); /* check if specific tag also specified - error in this case */ if (min_3_prime_distance_specific == 1) { pr_append_new_chunk(glob_err, "Both PRIMER_MIN_THREE_PRIME_DISTANCE and PRIMER_{LEFT/RIGHT}_MIN_THREE_PRIME_DISTANCE specified"); } else { min_3_prime_distance_global = 1; /* set up individual flags */ pa->min_left_three_prime_distance = min_three_prime_distance; pa->min_right_three_prime_distance = min_three_prime_distance; } continue; } if (COMPARE("PRIMER_MIN_LEFT_THREE_PRIME_DISTANCE")) { parse_int("PRIMER_MIN_LEFT_THREE_PRIME_DISTANCE", datum, &(pa->min_left_three_prime_distance), parse_err); /* check if global tag also specified - error in this case */ if (min_3_prime_distance_global == 1) { pr_append_new_chunk(glob_err, "Both PRIMER_MIN_THREE_PRIME_DISTANCE and PRIMER_{LEFT/RIGHT}_MIN_THREE_PRIME_DISTANCE specified"); } else { min_3_prime_distance_specific = 1; } continue; } if (COMPARE("PRIMER_MIN_RIGHT_THREE_PRIME_DISTANCE")) { parse_int("PRIMER_MIN_RIGHT_THREE_PRIME_DISTANCE", datum, &(pa->min_right_three_prime_distance), parse_err); /* check if global tag also specified - error in this case */ if (min_3_prime_distance_global == 1) { pr_append_new_chunk(glob_err, "Both PRIMER_MIN_THREE_PRIME_DISTANCE and PRIMER_{LEFT/RIGHT}_MIN_THREE_PRIME_DISTANCE specified"); } else { min_3_prime_distance_specific = 1; } continue; } if (file_type == settings) { if (COMPARE("P3_FILE_ID")) continue; } COMPARE_INT("PRIMER_QUALITY_RANGE_MIN", pa->quality_range_min); COMPARE_INT("PRIMER_QUALITY_RANGE_MAX", pa->quality_range_max); COMPARE_FLOAT("PRIMER_PRODUCT_MAX_TM", pa->product_max_tm); COMPARE_FLOAT("PRIMER_PRODUCT_MIN_TM", pa->product_min_tm); COMPARE_FLOAT("PRIMER_PRODUCT_OPT_TM", pa->product_opt_tm); COMPARE_INT("PRIMER_SEQUENCING_LEAD", pa->sequencing.lead); COMPARE_INT("PRIMER_SEQUENCING_SPACING", pa->sequencing.spacing); COMPARE_INT("PRIMER_SEQUENCING_INTERVAL", pa->sequencing.interval); COMPARE_INT("PRIMER_SEQUENCING_ACCURACY", pa->sequencing.accuracy); if (COMPARE("PRIMER_MIN_5_PRIME_OVERLAP_OF_JUNCTION")) { parse_int("PRIMER_MIN_5_PRIME_OVERLAP_OF_JUNCTION", datum, &pa->min_5_prime_overlap_of_junction, parse_err); /* min_5_prime = 1; Removed 10/20/2010 */ continue; } if (COMPARE("PRIMER_MIN_3_PRIME_OVERLAP_OF_JUNCTION")) { parse_int("PRIMER_MIN_3_PRIME_OVERLAP_OF_JUNCTION", datum, &pa->min_3_prime_overlap_of_junction, parse_err); /* min_3_prime = 1; Removed 10/20/2010 */ continue; } COMPARE_AND_MALLOC("PRIMER_TASK", task_tmp); COMPARE_INT("PRIMER_PICK_RIGHT_PRIMER", pa->pick_right_primer); COMPARE_INT("PRIMER_PICK_INTERNAL_OLIGO", pa->pick_internal_oligo); COMPARE_INT("PRIMER_PICK_LEFT_PRIMER", pa->pick_left_primer); COMPARE_INT("PRIMER_INTERNAL_OPT_SIZE", pa->o_args.opt_size); COMPARE_INT("PRIMER_INTERNAL_MAX_SIZE", pa->o_args.max_size); COMPARE_INT("PRIMER_INTERNAL_MIN_SIZE", pa->o_args.min_size); COMPARE_INT("PRIMER_INTERNAL_MAX_POLY_X", pa->o_args.max_poly_x); COMPARE_FLOAT("PRIMER_INTERNAL_OPT_TM", pa->o_args.opt_tm); COMPARE_FLOAT("PRIMER_INTERNAL_OPT_GC_PERCENT", pa->o_args.opt_gc_content); COMPARE_FLOAT("PRIMER_INTERNAL_MAX_TM", pa->o_args.max_tm); COMPARE_FLOAT("PRIMER_INTERNAL_MIN_TM", pa->o_args.min_tm); COMPARE_FLOAT("PRIMER_INTERNAL_MIN_GC", pa->o_args.min_gc); COMPARE_FLOAT("PRIMER_INTERNAL_MAX_GC", pa->o_args.max_gc); COMPARE_FLOAT("PRIMER_INTERNAL_SALT_MONOVALENT", pa->o_args.salt_conc); COMPARE_FLOAT("PRIMER_INTERNAL_SALT_DIVALENT", pa->o_args.divalent_conc); COMPARE_FLOAT("PRIMER_INTERNAL_DNTP_CONC", pa->o_args.dntp_conc); COMPARE_FLOAT("PRIMER_INTERNAL_DNA_CONC", pa->o_args.dna_conc); COMPARE_INT("PRIMER_INTERNAL_MAX_NS_ACCEPTED", pa->o_args.num_ns_accepted); COMPARE_INT("PRIMER_INTERNAL_MIN_QUALITY", pa->o_args.min_quality); COMPARE_FLOAT("PRIMER_INTERNAL_MAX_SELF_ANY", pa->o_args.max_self_any); /* COMPARE_FLOAT("PRIMER_INTERNAL_MAX_SELF_END", pa->o_args.max_self_end); */ /* NEW */ COMPARE_FLOAT_USE_FN("PRIMER_INTERNAL_MAX_SELF_END", p3_set_gs_primer_internal_oligo_self_end); /* if (COMPARE("PRIMER_INTERNAL_MAX_SELF_END")) { parse_double("PRIMER_INTERNAL_MAX_SELF_END", datum, &tmp_double, parse_err); p3_set_gs_primer_internal_oligo_self_end(pa, tmp_double); continue; } */ COMPARE_FLOAT("PRIMER_INTERNAL_MAX_SELF_ANY_TH", pa->o_args.max_self_any_th); COMPARE_FLOAT("PRIMER_INTERNAL_MAX_SELF_END_TH", pa->o_args.max_self_end_th); COMPARE_FLOAT("PRIMER_INTERNAL_MAX_HAIRPIN_TH", pa->o_args.max_hairpin_th); COMPARE_FLOAT("PRIMER_MAX_LIBRARY_MISPRIMING", pa->p_args.max_repeat_compl); COMPARE_FLOAT("PRIMER_INTERNAL_MAX_LIBRARY_MISHYB", pa->o_args.max_repeat_compl); COMPARE_FLOAT("PRIMER_PAIR_MAX_LIBRARY_MISPRIMING", pa->pair_repeat_compl); /* Mispriming / mishybing in the template. */ COMPARE_FLOAT("PRIMER_MAX_TEMPLATE_MISPRIMING", pa->p_args.max_template_mispriming); COMPARE_FLOAT("PRIMER_MAX_TEMPLATE_MISPRIMING_TH", pa->p_args.max_template_mispriming_th); COMPARE_FLOAT("PRIMER_PAIR_MAX_TEMPLATE_MISPRIMING", pa->pair_max_template_mispriming); COMPARE_FLOAT("PRIMER_PAIR_MAX_TEMPLATE_MISPRIMING_TH", pa->pair_max_template_mispriming_th); /* Control interpretation of ambiguity codes in mispriming and mishyb libraries. */ COMPARE_INT("PRIMER_LIB_AMBIGUITY_CODES_CONSENSUS", pa->lib_ambiguity_codes_consensus); COMPARE_FLOAT("PRIMER_INSIDE_PENALTY", pa->inside_penalty); COMPARE_FLOAT("PRIMER_OUTSIDE_PENALTY", pa->outside_penalty); if (COMPARE("PRIMER_MISPRIMING_LIBRARY")) { if (repeat_file_path != NULL) { pr_append_new_chunk(glob_err, "Duplicate PRIMER_MISPRIMING_LIBRARY tag"); free(repeat_file_path); repeat_file_path = NULL; } else { repeat_file_path = (char*) _rb_safe_malloc(strlen(datum) + 1); strcpy(repeat_file_path, datum); } continue; } if (COMPARE("PRIMER_INTERNAL_MISHYB_LIBRARY")) { if (int_repeat_file_path != NULL) { pr_append_new_chunk(glob_err, "Duplicate PRIMER_INTERNAL_MISHYB_LIBRARY tag"); free(int_repeat_file_path); int_repeat_file_path = NULL; } else { int_repeat_file_path = (char*) _rb_safe_malloc(strlen(datum) + 1); strcpy(int_repeat_file_path, datum); } continue; } if (COMPARE("P3_COMMENT")) continue; COMPARE_FLOAT("PRIMER_MAX_END_STABILITY", pa->max_end_stability); COMPARE_INT("PRIMER_LOWERCASE_MASKING", pa->lowercase_masking); /* added by T. Koressaar */ COMPARE_INT("PRIMER_THERMODYNAMIC_OLIGO_ALIGNMENT", pa->thermodynamic_oligo_alignment); COMPARE_INT("PRIMER_THERMODYNAMIC_TEMPLATE_ALIGNMENT", pa->thermodynamic_template_alignment); if (COMPARE("PRIMER_THERMODYNAMIC_PARAMETERS_PATH")) { if (thermodynamic_params_path == NULL) { thermodynamic_params_path = (char*) _rb_safe_malloc(datum_len + 1); strcpy(thermodynamic_params_path, datum); thermodynamic_path_changed = 1; } /* check if path changes */ else if (strcmp(thermodynamic_params_path, datum)) { free(thermodynamic_params_path); thermodynamic_params_path = (char*) _rb_safe_malloc(datum_len + 1); strcpy(thermodynamic_params_path, datum); thermodynamic_path_changed = 1; } continue; } if (COMPARE("PRIMER_MASK_KMERLIST_PATH")) { if (kmer_lists_path == NULL) { kmer_lists_path = (char*) _rb_safe_malloc(datum_len + 1); strcpy(kmer_lists_path, datum); } continue; } /* added by A. Untergasser */ OVERWRITE_COMPARE_AND_MALLOC("PRIMER_MUST_MATCH_FIVE_PRIME", pa->p_args.must_match_five_prime); OVERWRITE_COMPARE_AND_MALLOC("PRIMER_MUST_MATCH_THREE_PRIME", pa->p_args.must_match_three_prime); OVERWRITE_COMPARE_AND_MALLOC("PRIMER_INTERNAL_MUST_MATCH_FIVE_PRIME", pa->o_args.must_match_five_prime); OVERWRITE_COMPARE_AND_MALLOC("PRIMER_INTERNAL_MUST_MATCH_THREE_PRIME", pa->o_args.must_match_three_prime); /* weights for objective functions */ /* CHANGE TEMP/temp -> TM/tm */ COMPARE_FLOAT("PRIMER_WT_TM_GT", pa->p_args.weights.temp_gt); COMPARE_FLOAT("PRIMER_WT_TM_LT", pa->p_args.weights.temp_lt); COMPARE_FLOAT("PRIMER_WT_GC_PERCENT_GT", pa->p_args.weights.gc_content_gt); COMPARE_FLOAT("PRIMER_WT_GC_PERCENT_LT", pa->p_args.weights.gc_content_lt); COMPARE_FLOAT("PRIMER_WT_SIZE_LT", pa->p_args.weights.length_lt); COMPARE_FLOAT("PRIMER_WT_SIZE_GT", pa->p_args.weights.length_gt); COMPARE_FLOAT("PRIMER_WT_SELF_ANY", pa->p_args.weights.compl_any); COMPARE_FLOAT("PRIMER_WT_SELF_END", pa->p_args.weights.compl_end); COMPARE_FLOAT("PRIMER_WT_SELF_ANY_TH", pa->p_args.weights.compl_any_th); COMPARE_FLOAT("PRIMER_WT_SELF_END_TH", pa->p_args.weights.compl_end_th); COMPARE_FLOAT("PRIMER_WT_HAIRPIN_TH", pa->p_args.weights.hairpin_th); COMPARE_FLOAT("PRIMER_WT_NUM_NS", pa->p_args.weights.num_ns); COMPARE_FLOAT("PRIMER_WT_LIBRARY_MISPRIMING", pa->p_args.weights.repeat_sim); COMPARE_FLOAT("PRIMER_WT_SEQ_QUAL", pa->p_args.weights.seq_quality); COMPARE_FLOAT("PRIMER_WT_END_QUAL", pa->p_args.weights.end_quality); COMPARE_FLOAT("PRIMER_WT_POS_PENALTY", pa->p_args.weights.pos_penalty); COMPARE_FLOAT("PRIMER_WT_END_STABILITY", pa->p_args.weights.end_stability); COMPARE_FLOAT("PRIMER_WT_TEMPLATE_MISPRIMING", pa->p_args.weights.template_mispriming); COMPARE_FLOAT("PRIMER_WT_TEMPLATE_MISPRIMING_TH", pa->p_args.weights.template_mispriming_th); COMPARE_FLOAT("PRIMER_WT_MASK_FAILURE_RATE", pa->p_args.weights.failure_rate); COMPARE_FLOAT("PRIMER_INTERNAL_WT_TM_GT", pa->o_args.weights.temp_gt); COMPARE_FLOAT("PRIMER_INTERNAL_WT_TM_LT", pa->o_args.weights.temp_lt); COMPARE_FLOAT("PRIMER_INTERNAL_WT_GC_PERCENT_GT", pa->o_args.weights.gc_content_gt); COMPARE_FLOAT("PRIMER_INTERNAL_WT_GC_PERCENT_LT", pa->o_args.weights.gc_content_lt); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SIZE_LT", pa->o_args.weights.length_lt); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SIZE_GT", pa->o_args.weights.length_gt); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SELF_ANY", pa->o_args.weights.compl_any); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SELF_END", pa->o_args.weights.compl_end); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SELF_ANY_TH", pa->o_args.weights.compl_any_th); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SELF_END_TH", pa->o_args.weights.compl_end_th); COMPARE_FLOAT("PRIMER_INTERNAL_WT_HAIRPIN_TH", pa->o_args.weights.hairpin_th); COMPARE_FLOAT("PRIMER_INTERNAL_WT_NUM_NS", pa->o_args.weights.num_ns); COMPARE_FLOAT("PRIMER_INTERNAL_WT_LIBRARY_MISHYB", pa->o_args.weights.repeat_sim); COMPARE_FLOAT("PRIMER_INTERNAL_WT_SEQ_QUAL", pa->o_args.weights.seq_quality); COMPARE_FLOAT("PRIMER_INTERNAL_WT_END_QUAL", pa->o_args.weights.end_quality); COMPARE_FLOAT("PRIMER_WT_TEMPLATE_MISPRIMING_TH", pa->o_args.weights.template_mispriming_th); COMPARE_FLOAT("PRIMER_PAIR_WT_PR_PENALTY", pa->pr_pair_weights.primer_quality); COMPARE_FLOAT("PRIMER_PAIR_WT_IO_PENALTY", pa->pr_pair_weights.io_quality); COMPARE_FLOAT("PRIMER_PAIR_WT_DIFF_TM", pa->pr_pair_weights.diff_tm); COMPARE_FLOAT("PRIMER_PAIR_WT_COMPL_ANY", pa->pr_pair_weights.compl_any); COMPARE_FLOAT("PRIMER_PAIR_WT_COMPL_END", pa->pr_pair_weights.compl_end); COMPARE_FLOAT("PRIMER_PAIR_WT_COMPL_ANY_TH", pa->pr_pair_weights.compl_any_th); COMPARE_FLOAT("PRIMER_PAIR_WT_COMPL_END_TH", pa->pr_pair_weights.compl_end_th); COMPARE_FLOAT("PRIMER_PAIR_WT_PRODUCT_TM_LT", pa->pr_pair_weights.product_tm_lt); COMPARE_FLOAT("PRIMER_PAIR_WT_PRODUCT_TM_GT", pa->pr_pair_weights.product_tm_gt); COMPARE_FLOAT("PRIMER_PAIR_WT_PRODUCT_SIZE_GT", pa->pr_pair_weights.product_size_gt); COMPARE_FLOAT("PRIMER_PAIR_WT_PRODUCT_SIZE_LT", pa->pr_pair_weights.product_size_lt); COMPARE_FLOAT("PRIMER_PAIR_WT_LIBRARY_MISPRIMING", pa->pr_pair_weights.repeat_sim); COMPARE_FLOAT("PRIMER_PAIR_WT_TEMPLATE_MISPRIMING", pa->pr_pair_weights.template_mispriming); COMPARE_FLOAT("PRIMER_PAIR_WT_TEMPLATE_MISPRIMING_TH", pa->pr_pair_weights.template_mispriming_th); if (COMPARE("PRIMER_MASK_TEMPLATE")) { parse_int("PRIMER_MASK_TEMPLATE", datum, &pa->mask_template, parse_err); pa->lowercase_masking = pa->mask_template; pa->masking_parameters_changed = pa->mask_template; continue; } COMPARE_FLOAT("PRIMER_MASK_FAILURE_RATE", pa->mp.failure_rate); COMPARE_INT("PRIMER_MASK_5P_DIRECTION", pa->mp.nucl_masked_in_5p_direction); COMPARE_INT("PRIMER_MASK_3P_DIRECTION", pa->mp.nucl_masked_in_3p_direction); if (COMPARE("PRIMER_MASK_KMERLIST_PREFIX")) { if(pa->mp.list_prefix == NULL){ pa->mp.list_prefix = (char*) _rb_safe_malloc(datum_len + 1); strcpy(pa->mp.list_prefix, datum); pa->masking_parameters_changed = 1; } else if (strcmp(pa->mp.list_prefix, datum)){ free(pa->mp.list_prefix); pa->mp.list_prefix = (char*) _rb_safe_malloc(datum_len + 1); strcpy(pa->mp.list_prefix, datum); pa->masking_parameters_changed = 1; } continue; } } /* End of reading the tags in the right place */ /* Complain about unrecognized tags */ if (*strict_tags == 1) { pr_append_new_chunk(glob_err, "Unrecognized tag: "); pr_append(glob_err, s); fprintf(stderr, "Unrecognized tag: %s\n", s); } } /* while ((s = p3_read_line(stdin)) != NULL && strcmp(s,"=")) { */ /* Check if the record was terminated by "=" */ if (NULL == s) { /* End of file. */ if (data_found) { pr_append_new_chunk(glob_err, "Final record not terminated by '='"); return 1; } else return 0; } /* Figure out the right settings for the tasks*/ if (task_tmp != NULL) { if (!strcmp_nocase(task_tmp, "pick_pcr_primers")) { pa->primer_task = generic; pa->pick_left_primer = 1; pa->pick_right_primer = 1; pa->pick_internal_oligo = 0; } else if (!strcmp_nocase(task_tmp, "pick_pcr_primers_and_hyb_probe")) { pa->primer_task = generic; pa->pick_left_primer = 1; pa->pick_right_primer = 1; pa->pick_internal_oligo = 1; } else if (!strcmp_nocase(task_tmp, "pick_left_only")) { pa->primer_task = generic; pa->pick_left_primer = 1; pa->pick_right_primer = 0; pa->pick_internal_oligo = 0; } else if (!strcmp_nocase(task_tmp, "pick_right_only")) { pa->primer_task = generic; pa->pick_left_primer = 0; pa->pick_right_primer = 1; pa->pick_internal_oligo = 0; } else if (!strcmp_nocase(task_tmp, "pick_hyb_probe_only")) { pa->primer_task = generic; pa->pick_left_primer = 0; pa->pick_right_primer = 0; pa->pick_internal_oligo = 1; } else if (!strcmp_nocase(task_tmp, "generic")) { pa->primer_task = generic; } else if (!strcmp_nocase(task_tmp, "pick_detection_primers")) { pa->primer_task = generic; /* Deliberate duplication for backward compatibility. */ } else if (!strcmp_nocase(task_tmp, "pick_cloning_primers")) { pa->primer_task = pick_cloning_primers; } else if (!strcmp_nocase(task_tmp, "pick_discriminative_primers")) { pa->primer_task = pick_discriminative_primers; } else if (!strcmp_nocase(task_tmp, "pick_sequencing_primers")) { pa->primer_task = pick_sequencing_primers; } else if (!strcmp_nocase(task_tmp, "pick_primer_list")) { pa->primer_task = pick_primer_list; } else if (!strcmp_nocase(task_tmp, "check_primers")) { pa->primer_task = check_primers; /* check_primers sets the picking flags itself */ pa->pick_left_primer = 0; pa->pick_right_primer = 0; pa->pick_internal_oligo = 0; if (sa->left_input){ pa->pick_left_primer = 1; } if (sa->right_input){ pa->pick_right_primer = 1; } if (sa->internal_input){ pa->pick_internal_oligo = 1; } } else pr_append_new_chunk(glob_err, "Unrecognized PRIMER_TASK"); free(task_tmp); } /* * WARNING: read_and_create_seq_lib uses p3_read_line, so repeat * library files cannot be read inside the * while ((s = p3_read_line(stdin))...) loop above. * * FIX ME, in fact the reading * of the library contents probably belongs inside * primer3_boulder_main.c or libprimer3.c. */ /* Read in the repeat libraries */ if (NULL != repeat_file_path) { destroy_seq_lib(pa->p_args.repeat_lib); if ('\0' == *repeat_file_path) { /* Input now specifies no repeat library. */ pa->p_args.repeat_lib = NULL; } else { pa->p_args.repeat_lib = read_and_create_seq_lib(repeat_file_path, "mispriming library"); if(pa->p_args.repeat_lib->error.data != NULL) { pr_append_new_chunk(glob_err, pa->p_args.repeat_lib->error.data); } } free(repeat_file_path); repeat_file_path = NULL; } /* Read in the repeat libraries for internal oligo */ if (NULL != int_repeat_file_path) { destroy_seq_lib(pa->o_args.repeat_lib); if ('\0' == *int_repeat_file_path) { /* Input now specifies no mishybridization library. */ pa->o_args.repeat_lib = NULL; } else { pa->o_args.repeat_lib = read_and_create_seq_lib(int_repeat_file_path, "internal oligo mishyb library"); if(pa->o_args.repeat_lib->error.data != NULL) { pr_append_new_chunk(glob_err, pa->o_args.repeat_lib->error.data); } } free(int_repeat_file_path); int_repeat_file_path = NULL; } if (pa->primer_task == pick_pcr_primers_and_hyb_probe) { PR_ASSERT(pa->pick_internal_oligo); } /* Removed 10/20/2010 as excessively compulsive, especially in the context of taking input from web pages. if ((min_3_prime || min_5_prime) && (sa->primer_overlap_junctions_count == 0)) { pr_append_new_chunk(warnings, "SEQUENCE_OVERLAP_JUNCTION_LIST not given, but " "PRIMER_MIN_3_PRIME_OVERLAP_OF_JUNCTION or " "PRIMER_MIN_5_PRIME_OVERLAP_OF_JUNCTION specified"); } */ return 1; } #undef COMPARE #undef COMPARE_AND_MALLOC #undef COMPARE_INT #undef COMPARE_FLOAT #undef COMPARE_INTERVAL_LIST static void pr_append2(pr_append_str *err, const char* s1, const char* s2) { pr_append_new_chunk(err, s1); pr_append(err, s2); } int read_p3_file(const char *file_name, const p3_file_type expected_file_type, int echo_output, int strict_tags, p3_global_settings *pa, seq_args *sa, pr_append_str *fatal_err, pr_append_str *nonfatal_err, pr_append_str *warnings, read_boulder_record_results *read_boulder_record_res) { FILE *file; int ret_par = 0; int io_version = 4; char *line1; char *line2; char *line3; p3_file_type file_type = all_parameters; /* Check if a file name was provided */ PR_ASSERT(NULL != file_name); /* Open the file */ if (!(file = fopen(file_name,"r"))) { pr_append2(fatal_err, "Cannot open ", file_name); return ret_par; } /* Parse and interpret the first 3 lines */ /* Line 1 */ line1 = p3_read_line(file); if (!line1) { pr_append2(fatal_err, "Settings file is empty: ", file_name); return ret_par; } if ((strcmp(line1,"Primer3 File - http://primer3.org") != 0) && (strcmp(line1,"Primer3 File - http://primer3.sourceforge.net") != 0)) { pr_append2(fatal_err, "First line must be \"Primer3 File - http://primer3.org\" in ", file_name); return ret_par; } /* Line 2 */ line2 = p3_read_line(file); if (!line2) { pr_append2(fatal_err, "Incorrect file format (too few lines) in ", file_name); return ret_par; } if (!strcmp(line2,"P3_FILE_TYPE=all_parameters")) { file_type = all_parameters; } else if (!strcmp(line2,"P3_FILE_TYPE=sequence")) { file_type = sequence; } else if (!strcmp(line2,"P3_FILE_TYPE=settings")) { file_type = settings; } else { pr_append2(fatal_err, "Unknown file type in at line 2 (line2='", line2); pr_append(fatal_err, "') in "); pr_append(fatal_err, file_name); return ret_par; } if (echo_output) { printf("P3_SETTINGS_FILE_USED=%s\n", file_name); printf("%s\n", line2); } /* Line 3 */ line3 = p3_read_line(file); if (!line3) { pr_append2(fatal_err, "Incorrect file format (too few lines) in ", file_name); return ret_par; } if (strcmp(line3, "")) { pr_append2(fatal_err, "Line 3 must be empty in ", file_name); return ret_par; } /* Check if the file type matches the expected type */ if (file_type != expected_file_type){ pr_append_new_chunk(nonfatal_err, "Unexpected P3 file type parsed"); } /* read the file */ ret_par = read_boulder_record(file, &strict_tags, &io_version, echo_output, expected_file_type, pa, sa, fatal_err, nonfatal_err, warnings, read_boulder_record_res); if(pa->mask_template){ pa->lowercase_masking=pa->mask_template; } if (echo_output) printf("P3_SETTINGS_FILE_END=\n"); if (file) fclose(file); return ret_par; } static void tag_syntax_error(const char *tag_name, const char *datum, pr_append_str *err) { pr_append_new_chunk(err, "Illegal "); pr_append(err, tag_name); pr_append(err, " value: "); pr_append(err, datum); } static void parse_double(const char *tag_name, const char *datum, double *out, pr_append_str *err) { char *nptr; *out = strtod(datum, &nptr); if (nptr == datum) { /* Empty string or complete junk. */ tag_syntax_error(tag_name, datum, err); *out = 0.0; return; } /* Look for trailing junk. */ while (*nptr != '\n' && *nptr != '\0') { if (*nptr != ' ' && *nptr != '\t') { tag_syntax_error(tag_name, datum, err); break; } nptr++; } } static void parse_int(const char *tag_name, const char *datum, int *out, pr_append_str *err) { char *nptr; long tlong; tlong = strtol(datum, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return; } *out = tlong; if (nptr == datum) { /* Empty string or complete junk. */ tag_syntax_error(tag_name, datum, err); return; } /* Look for trailing junk. */ while (*nptr != '\n' && *nptr != '\0') { if (*nptr != ' ' && *nptr != '\t') { tag_syntax_error(tag_name, datum, err); break; } nptr++; } } /* * For correct input, return a pointer to the first non-tab, non-space * character after the second integer, and place the integers in out1 and * out2. On incorrect input, return NULL; */ static const char * parse_int_pair(const char *tag_name, const char *datum, char sep, /* The separator, e.g. ',' or '-'. */ int *out1, int *out2, /* The 2 integers. */ pr_append_str *err) /* Error messages. */ { char *nptr, *tmp; long tlong; tlong = strtol(datum, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return NULL; } *out1 = tlong; if (nptr == datum) { tag_syntax_error(tag_name, datum, err); return NULL; } while (' ' == *nptr || '\t' == *nptr) nptr++; if (sep != *nptr) { tag_syntax_error(tag_name, datum, err); return NULL; } nptr++; /* Advance past separator. */ while (' ' == *nptr || '\t' == *nptr) nptr++; tmp = nptr; tlong = strtol(tmp, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return NULL; } *out2 = tlong; if (nptr == tmp) { tag_syntax_error(tag_name, datum, err); return NULL; } while (' ' == *nptr || '\t' == *nptr) nptr++; /* A hack to live with the old TARGET syntax. */ if (',' == *nptr && !strcmp(tag_name, "TARGET")) { /* Skip the old-fashioned "description". */ while(' ' != *nptr && '\t' != *nptr && '\0' != *nptr && '\n' != *nptr) nptr++; /* Advance to non-space, non-tab. */ while (' ' == *nptr || '\t' == *nptr) nptr++; } return nptr; } static void parse_interval_list(const char *tag_name, const char *datum, interval_array_t2 *interval_arr, pr_append_str *err) { const char *p = datum; int i1, i2; int ret = 0; while (' ' == *p || '\t' == *p) p++; while (*p != '\0' && *p != '\n') { p = parse_int_pair(tag_name, p, ',', &i1, &i2, err); if (NULL == p) return; ret = p3_add_to_interval_array(interval_arr, i1, i2); if (ret) { pr_append_new_chunk(err, "Too many elements for tag "); pr_append(err, tag_name); return; } } } /* * For correct input, return a pointer to the first non-tab, non-space * character after the forth integer and after the separator sep2, and * place the integers in out1, out2, out3 and out4. On incorrect input, * return NULL; If any of the 4 integers is not specified, the * corresponding output value will be -1. */ static char * parse_2_int_pair(const char *tag_name, char *datum, char sep, /* The separator between 2 numbers, e.g. ',' or '-'. */ char sep2, /* Separator between 2 intervals */ int *out1, int *out2, int *out3, int *out4, /* The 4 integers. */ pr_append_str *err) /* Error messages. */ { char *nptr, *tmp; long tlong; nptr = datum; if (*nptr == sep) { *out1 = -1; } else { tlong = strtol(datum, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return NULL; } *out1 = tlong; if (nptr == datum) { tag_syntax_error(tag_name, datum, err); return NULL; } } while (' ' == *nptr || '\t' == *nptr) nptr++; if (sep != *nptr) { tag_syntax_error(tag_name, datum, err); return NULL; } nptr++; /* Advance past separator. */ while (' ' == *nptr || '\t' == *nptr) nptr++; tmp = nptr; if (*nptr == sep) { *out2 = -1; } else { tlong = strtol(tmp, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return NULL; } *out2 = tlong; if (nptr == tmp) { tag_syntax_error(tag_name, datum, err); return NULL; } } while (' ' == *nptr || '\t' == *nptr) nptr++; if (sep != *nptr) { tag_syntax_error(tag_name, datum, err); return NULL; } nptr++; /* Advance past separator. */ while (' ' == *nptr || '\t' == *nptr) nptr++; tmp = nptr; if (*nptr == sep) { *out3 = -1; } else { tlong = strtol(tmp, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return NULL; } *out3 = tlong; if (nptr == tmp) { tag_syntax_error(tag_name, datum, err); return NULL; } } while (' ' == *nptr || '\t' == *nptr) nptr++; if (sep != *nptr) { tag_syntax_error(tag_name, datum, err); return NULL; } nptr++; /* Advance past separator. */ while (' ' == *nptr || '\t' == *nptr) nptr++; tmp = nptr; if ((*nptr == '\n') || (*nptr == '\0') || (*nptr == sep2)) { *out4 = -1; } else { tlong = strtol(tmp, &nptr, 10); if (tlong > INT_MAX || tlong < INT_MIN) { tag_syntax_error(tag_name, datum, err); pr_append(err, " (value too large or too small)"); return NULL; } *out4 = tlong; if (nptr == tmp) { tag_syntax_error(tag_name, datum, err); return NULL; } } while (' ' == *nptr || '\t' == *nptr) nptr++; /* Should have reached an interval separator or end of line/string */ if ((*nptr != sep2) && (*nptr != '\0') && (*nptr != '\n')) { tag_syntax_error(tag_name, datum, err); return NULL; } if (*nptr == sep2) { /* Advance past the interval separator */ nptr++; } while (' ' == *nptr || '\t' == *nptr) nptr++; return nptr; } static void parse_2_interval_list(const char *tag_name, char *datum, interval_array_t4 *interval_arr, pr_append_str *err) { char *p = datum; int i1, i2, i3, i4; int ret = 0; interval_arr->count = 0; interval_arr->any_pair = 0; interval_arr->any_left = interval_arr->any_right = 0; while (' ' == *p || '\t' == *p) p++; while (*p != '\0' && *p != '\n') { p = parse_2_int_pair(tag_name, p, ',', ';', &i1, &i2, &i3, &i4, err); if (NULL == p) return; ret = p3_add_to_2_interval_array(interval_arr, i1, i2, i3, i4); if (ret == 1) { pr_append_new_chunk(err, "Too many elements for tag "); pr_append(err, tag_name); return; } else if (ret == 2) { pr_append_new_chunk(err, "Invalid range at tag "); pr_append(err, tag_name); return; } } } static int parse_intron_list(char *s, int *list, int *count) { long t; char *p, *q; *count = 0; p = q = s; while (*q != '\0' && *q != '\n') { t = strtol(p, &q, 10); if (q == p) { while (*q != '\0') { if (!isspace(*q)) { *count = 0; return 0; } q++; } return *count; } if (t > INT_MAX || t < INT_MIN) { return 0; } list[*count] = t; (*count)++; p = q; } return *count; } static void parse_product_size(const char *tag_name, char *in, p3_global_settings *pa, pr_append_str *err) { char *q, *s = in; const char *p; int i; /* * Handle possible double quotes around the value. * (This handling is needed for backward compatibility with v2.) */ if ('"' == *s) { s++; in++; q = strchr(s, '"'); if (NULL == q) { pr_append_new_chunk(err, tag_name); pr_append(err, " begins but does not end with a quote"); return; } /* Ignore the " and everything after it. */ *q = '\0'; } p = in; while (' ' == *p || '\t' == *p) p++; i = 0; while (*p != '\0' && *p != '\n') { if (i >= PR_MAX_INTERVAL_ARRAY) { pr_append_new_chunk(err, "Too many values for "); pr_append(err, tag_name); return; } p = parse_int_pair(tag_name, p, '-', &pa->pr_min[i], &pa->pr_max[i], err); if (NULL == p) return; i++; } pa->num_intervals = i; } /* Would be worthwhile to test this a bit more off-by-one errors on length of the quality score */ /* This function returns the number of elements in the sequence quality vector, and updates sargs->quality, sargs->n_quality, and sargs->quality_storage_size */ static int parse_seq_quality(char *s, seq_args *sargs) { long t; char *p, *q; p3_set_sa_empty_quality(sargs); p = q = s; while (*q != '\0' && *q != '\n') { t = strtol(p, &q, 10); if (q == p) { while (*q != '\0') { if (!isspace(*q)) { p3_set_sa_empty_quality(sargs); return 0; } q++; } return sargs->n_quality; } p3_sa_add_to_quality_array(sargs, t); p = q; } return sargs->n_quality; } /* =========================================================== */ /* Fail-stop wrapper for memory allocation. */ /* =========================================================== */ /* * Panic messages for when the program runs out of memory. */ static void * _rb_safe_malloc(size_t x) { void *r = malloc(x); if (NULL == r) out_of_memory_error(); return r; } static void pr_append(pr_append_str *x, const char *s) { if (pr_append_external(x, s)) out_of_memory_error(); } static void pr_append_new_chunk(pr_append_str *x, const char *s) { if (pr_append_new_chunk_external(x, s)) out_of_memory_error(); } static void out_of_memory_error() { fprintf(stderr, "out of memory in read_boulder\n"); exit(-2); } /* End of fail-stop wrappers. */ /* =========================================================== */