1 /*
2 Copyright (c) 1996,1997,1998,1999,2000,2001,2004,2006,2007,2008,2009,
3 2010,2011,2012
4 Whitehead Institute for Biomedical Research, Steve Rozen
5 (http://purl.com/STEVEROZEN/), Andreas Untergasser and Helen Skaletsky
6 All rights reserved.
7
8 This file is part of primer3 and the libprimer3 library.
9
10 Primer3 and the libprimer3 library are free software;
11 you can redistribute them and/or modify them under the terms
12 of the GNU General Public License as published by the Free
13 Software Foundation; either version 2 of the License, or (at
14 your option) any later version.
15
16 This software is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this software (file gpl-2.0.txt in the source
23 distribution); if not, write to the Free Software
24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <signal.h>
40 #include <ctype.h>
41 #include <string.h> /* strcmp() */
42 #include <stdlib.h> /* free() */
43 #include <getopt.h> /* getopt() */
44 #include <unistd.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include "thal.h"
48 #include "format_output.h"
49 #include "libprimer3.h"
50 #include "read_boulder.h"
51 #include "print_boulder.h"
52
53 /* Check on which OS we compile */
54 #if defined(_WIN32) || defined(WIN32) || defined (__WIN32__) || defined(__CYGWIN__) || defined(__MINGW32__)
55 #define OS_WIN
56 #endif
57
58 /* Some function prototypes */
59 static void print_usage();
60 static void sig_handler(int);
61 static void read_thermodynamic_parameters();
62
63 /* Other global variables. */
64 static const char *pr_release;
65 static const char *pr_program_name;
66
67 int
main(int argc,char * argv[])68 main(int argc, char *argv[])
69 {
70 /* Setup the input data structures handlers */
71 int format_output = 0;
72 int strict_tags = 0;
73 int echo_settings = 0;
74 int io_version = 4;
75 int default_version = 2;
76 int dump_args = 0;
77
78 p3_global_settings *global_pa;
79 seq_args *sarg;
80 read_boulder_record_results read_boulder_record_res = {0,0};
81
82 pr_append_str p3_settings_path;
83 pr_append_str output_path;
84 pr_append_str error_path;
85 pr_append_str fatal_parse_err;
86 pr_append_str nonfatal_parse_err;
87 pr_append_str warnings;
88
89 /* Some variables needed by getopt */
90 int opt, option_index = 0;
91 struct option long_options[] = {
92 {"about", no_argument, 0, 'a'},
93 {"format_output", no_argument, &format_output, 1},
94 {"strict_tags", no_argument, &strict_tags, 1},
95 {"p3_settings_file", required_argument, 0, 'p'},
96 {"echo_settings_file", no_argument, &echo_settings, 1},
97 {"io_version", required_argument, 0, 'i'},
98 {"default_version", required_argument, 0, 'd'},
99 {"Dump_args", no_argument, 0, 'D'},
100 {"2x_compat", no_argument, 0, '2'},
101 {"output", required_argument, 0, 'o'},
102 {"error", required_argument, 0, 'e'},
103 {0, 0, 0, 0}
104 };
105 int about = 0, compat = 0, invalid_flag = 0;
106
107 /* Retval will point to the return value from choose_primers(). */
108 p3retval *retval = NULL;
109 int input_found=0;
110
111 init_pr_append_str(&fatal_parse_err);
112 init_pr_append_str(&nonfatal_parse_err);
113 init_pr_append_str(&warnings);
114 init_pr_append_str(&p3_settings_path);
115 init_pr_append_str(&output_path);
116 init_pr_append_str(&error_path);
117
118 /* Get the program name for correct error messages */
119 pr_release = libprimer3_release();
120 pr_program_name = argv[0];
121 p3_set_program_name(pr_program_name);
122
123 /* We set up some signal handlers in case someone starts up the program
124 * from the command line, wonders why nothing is happening, and then kills
125 * the program. */
126 signal(SIGINT, sig_handler);
127 signal(SIGTERM, sig_handler);
128
129 /* Read in the flags provided with the program call */
130 opterr = 0;
131 while ((opt = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) {
132 switch (opt) {
133 case 'a':
134 about = 1;
135 break;
136 case 'p':
137 if (pr_append_external(&p3_settings_path, optarg)) {
138 exit(-2); /* Out of memory. */
139 }
140 break;
141 case 'i':
142 if (!strcmp(optarg, "4"))
143 io_version = 4;
144 else
145 io_version = -1;
146 break;
147
148 case 'd': /* default_version */
149 if (!strcmp(optarg, "1"))
150 default_version = 1;
151 else if (!strcmp(optarg, "2"))
152 default_version = 2;
153 else
154 default_version = -1;
155 break;
156
157 case 'D': /* Undocumented flag for testing; causes
158 values of arguments to be echoed to
159 stdout. */
160 dump_args = 1;
161 break;
162 case '2':
163 compat = 1;
164 break;
165 case 'o':
166 if (pr_append_external(&output_path, optarg)) {
167 exit(-2); /* Out of memory. */
168 }
169
170 break;
171 case 'e':
172 if (pr_append_external(&error_path, optarg)) {
173 exit(-2); /* Out of memory. */
174 }
175
176 break;
177 case '?':
178 invalid_flag = 1;
179 break;
180 }
181 }
182 /* Open the output and error files specified */
183
184 if (!pr_is_empty(&error_path)) {
185 /* reassign stderr */
186 if (freopen(pr_append_str_chars(&error_path), "w", stderr)
187 == NULL) {
188 fprintf(stderr, "Error creating file %s\n",
189 pr_append_str_chars(&error_path));
190 exit(-1);
191 }
192 destroy_pr_append_str_data(&error_path);
193 }
194
195 if (!pr_is_empty(&output_path)) {
196 /* reassign stdout */
197 if (freopen(pr_append_str_chars(&output_path), "w", stdout)
198 == NULL) {
199 fprintf(stderr, "Error creating file %s\n",
200 pr_append_str_chars(&output_path));
201 exit(-1);
202 }
203 destroy_pr_append_str_data(&output_path);
204 }
205 /* We do any printing after redirecting stdout and stderr */
206 if (about == 1) {
207 printf("%s\n", pr_release);
208 exit(0);
209 }
210 if ((io_version == -1) || (invalid_flag == 1) || (default_version == -1)) {
211 print_usage();
212 exit(-1);
213 }
214 if (compat == 1) {
215 printf("PRIMER_ERROR=flag -2x_compat is no longer supported\n=\n");
216 exit(-1);
217 }
218
219 /* Check if an input file has been specified on the command line. */
220 if (optind < argc) {
221 if (optind + 1 != argc) {
222 print_usage();
223 exit(-1);
224 }
225 if (freopen(argv[optind], "r", stdin) == NULL) {
226 fprintf(stderr, "Error opening file %s\n", argv[optind]);
227 exit(-1);
228 }
229 }
230
231 /* Allocate the space for global settings and fill in default parameters */
232 if (default_version == 1)
233 global_pa = p3_create_global_settings_default_version_1();
234 else if (default_version == 2)
235 global_pa = p3_create_global_settings();
236 else {
237 print_usage();
238 exit(-1);
239 }
240
241 if (!global_pa) {
242 exit(-2); /* Out of memory. */
243 }
244 global_pa->dump = dump_args ;
245
246 /* Settings files have to be read in just below, and
247 the functions need a temporary sarg */
248 if (!(sarg = create_seq_arg())) {
249 exit(-2);
250 }
251
252 /* Read data from the settings file until a "=" line occurs. Assign
253 parameter values for primer picking to pa and sa. */
254 if (!pr_is_empty(&p3_settings_path)) {
255 read_p3_file(pr_append_str_chars(&p3_settings_path),
256 settings,
257 echo_settings && !format_output,
258 strict_tags,
259 global_pa, sarg, &fatal_parse_err,
260 &nonfatal_parse_err, &warnings, &read_boulder_record_res);
261 destroy_pr_append_str_data(&p3_settings_path);
262 /* Check if any thermodynamical alignment flag was given */
263 if ((global_pa->thermodynamic_oligo_alignment == 1) ||
264 (global_pa->thermodynamic_template_alignment == 1))
265 read_thermodynamic_parameters();
266 }
267
268 /* We also need to print out errors here because the loop erases all
269 errors at start. If there are fatal errors, write the proper
270 message and exit */
271 if (fatal_parse_err.data != NULL) {
272 if (format_output) {
273 format_error(stdout, sarg->sequence_name, fatal_parse_err.data);
274 } else {
275 print_boulder_error(fatal_parse_err.data);
276 }
277 fprintf(stderr, "%s: %s\n",
278 pr_program_name, fatal_parse_err.data);
279 destroy_seq_args(sarg);
280 exit(-4);
281 }
282
283 /* If there are nonfatal errors, write the proper message
284 * and skip to the end of the loop */
285 if (!pr_is_empty(&nonfatal_parse_err)) {
286 if (format_output) {
287 format_error(stdout, sarg->sequence_name,
288 nonfatal_parse_err.data);
289 } else {
290 print_boulder_error(nonfatal_parse_err.data);
291 }
292 }
293
294 /* The temporary sarg is not needed any more */
295 destroy_seq_args(sarg);
296 sarg = NULL;
297
298 /* Read the data from input stream record by record and process it if
299 * there are no errors. This is where the work is done. */
300 while (1) {
301 /* Create and initialize a seq_args data structure. sa (seq_args *) is
302 * initialized here because Values are _not_ retained across different
303 * input records. */
304 if (!(sarg = create_seq_arg())) {
305 exit(-2);
306 }
307
308 /* Reset all errors handlers and the return structure */
309 pr_set_empty(&fatal_parse_err);
310 pr_set_empty(&nonfatal_parse_err);
311 pr_set_empty(&warnings);
312 retval = NULL;
313
314 /* See read_boulder.h for documentation on read_boulder_record().*/
315 if (!read_boulder_record(stdin,
316 &strict_tags,
317 &io_version,
318 !format_output,
319 all_parameters,
320 global_pa,
321 sarg,
322 &fatal_parse_err,
323 &nonfatal_parse_err,
324 &warnings,
325 &read_boulder_record_res)) {
326 break; /* There were no more boulder records */
327 }
328
329 /* Check if any thermodynamical alignment flag was given and the
330 path to the parameter files changed - we need to reread them */
331 if (((global_pa->thermodynamic_oligo_alignment == 1) ||
332 (global_pa->thermodynamic_template_alignment == 1))
333 && (thermodynamic_path_changed == 1))
334 read_thermodynamic_parameters();
335
336 /* Check that we found the thermodynamic parameters in case any thermodynamic flag was set to 1. */
337 if (((global_pa->thermodynamic_oligo_alignment == 1) ||
338 (global_pa->thermodynamic_template_alignment == 1))
339 && (thermodynamic_params_path == NULL)) {
340 /* no parameter directory found, error */
341 printf("PRIMER_ERROR=thermodynamic approach chosen, but path to thermodynamic parameters not specified\n=\n");
342 exit(-1);
343 }
344
345 input_found = 1;
346 if ((global_pa->primer_task == generic)
347 && (global_pa->pick_internal_oligo == 1)){
348 PR_ASSERT(global_pa->pick_internal_oligo);
349 }
350
351 /* If there are fatal errors, write the proper message and exit */
352 if (fatal_parse_err.data != NULL) {
353 if (format_output) {
354 format_error(stdout, sarg->sequence_name, fatal_parse_err.data);
355 } else {
356 print_boulder_error(fatal_parse_err.data);
357 }
358 fprintf(stderr, "%s: %s\n",
359 pr_program_name, fatal_parse_err.data);
360 destroy_p3retval(retval);
361 destroy_seq_args(sarg);
362 exit(-4);
363 }
364
365 /* If there are nonfatal errors, write the proper message
366 * and skip to the end of the loop */
367 if (!pr_is_empty(&nonfatal_parse_err)) {
368 if (format_output) {
369 format_error(stdout, sarg->sequence_name,
370 nonfatal_parse_err.data);
371 } else {
372 print_boulder_error(nonfatal_parse_err.data);
373 }
374 goto loop_wrap_up;
375 }
376
377 /* Print any warnings and continue processing */
378 if (!pr_is_empty(&warnings)) {
379 if (format_output) {
380 format_warning(stdout, sarg->sequence_name,
381 warnings.data);
382 } else {
383 print_boulder_warning(warnings.data);
384 }
385 }
386
387 if (read_boulder_record_res.file_flag && sarg->sequence_name == NULL) {
388 /* We will not have a base name for the files */
389 if (format_output) {
390 format_error(stdout, NULL,
391 "Need PRIMER_SEQUENCE_ID if PRIMER_FILE_FLAG is not 0");
392 } else {
393 print_boulder_error("Need PRIMER_SEQUENCE_ID if PRIMER_FILE_FLAG is not 0");
394 }
395 goto loop_wrap_up;
396 }
397
398 /* Pick the primers - the central function */
399 p3_set_gs_primer_file_flag(global_pa,
400 read_boulder_record_res.file_flag);
401 retval = choose_primers(global_pa, sarg);
402 if (NULL == retval) exit(-2); /* Out of memory. */
403
404 /* If it was necessary to use a left_input, right_input,
405 or internal_oligo_input primer that was
406 unacceptable, then add warnings. */
407 if (global_pa->pick_anyway && format_output) {
408 if (sarg->left_input) {
409 add_must_use_warnings(&retval->warnings,
410 "Left primer", &retval->fwd.expl);
411 }
412 if (sarg->right_input) {
413 add_must_use_warnings(&retval->warnings,
414 "Right primer", &retval->rev.expl);
415 }
416 if (sarg->internal_input) {
417 add_must_use_warnings(&retval->warnings,
418 "Hybridization probe", &retval->intl.expl);
419 }
420 }
421
422 if (pr_is_empty(&retval->glob_err)
423 && pr_is_empty(&retval->per_sequence_err)) {
424 /* We need to test for errors before we call
425 p3_print_oligo_lists. This function only works on retval as
426 returned when there were no errors. */
427 if (read_boulder_record_res.file_flag) {
428 /* Create files with left, right, and internal oligos. */
429 p3_print_oligo_lists(retval, sarg, global_pa,
430 &retval->per_sequence_err,
431 sarg->sequence_name);
432 }
433 }
434
435 if (format_output) {
436 print_format_output(stdout, &io_version, global_pa,
437 sarg, retval, pr_release,
438 read_boulder_record_res.explain_flag);
439 } else {
440 /* Use boulder output */
441 print_boulder(io_version, global_pa, sarg, retval,
442 read_boulder_record_res.explain_flag);
443 }
444
445 loop_wrap_up: /* Here the failed loops join in again */
446 if (NULL != retval) {
447 /* Check for errors and print them */
448 if (NULL != retval->glob_err.data) {
449 fprintf(stderr, "%s: %s\n", pr_program_name, retval->glob_err.data);
450 destroy_p3retval(retval);
451 destroy_seq_args(sarg);
452 exit(-4);
453 }
454 }
455 destroy_p3retval(retval); /* This works even if retval is NULL */
456 retval = NULL;
457 destroy_seq_args(sarg);
458 sarg = NULL;
459
460 } /* while (1) (processing boulder io records) ...
461 End of the primary working loop */
462
463 /* To avoid being distracted when looking for leaks: */
464 if ((global_pa->thermodynamic_oligo_alignment == 1) ||
465 (global_pa->thermodynamic_template_alignment == 1))
466 destroy_thal_structures();
467 p3_destroy_global_settings(global_pa);
468 global_pa = NULL;
469 destroy_seq_args(sarg);
470 destroy_pr_append_str_data(&nonfatal_parse_err);
471 destroy_pr_append_str_data(&fatal_parse_err);
472 destroy_pr_append_str_data(&warnings);
473 destroy_dpal_thal_arg_holder();
474 free(thermodynamic_params_path);
475 /* If it could not read input, then complain and die */
476 if (0 == input_found) {
477 print_usage();
478 exit(-3);
479 }
480 return 0;
481 }
482
483 /* Reads the thermodynamic parameters if the thermodynamic alignment
484 tag was set to 1 */
485 static void
read_thermodynamic_parameters()486 read_thermodynamic_parameters()
487 {
488 thal_results o;
489 /* if the path to the parameter files did not change,
490 we do not want to read again */
491 if (thermodynamic_path_changed == 0) return;
492 /* check that the path to the parameters folder was given */
493 if (thermodynamic_params_path == NULL) {
494
495 #ifdef OS_WIN
496 /* in windows check for .\\primer3_config */
497 struct stat st;
498 if ((stat(".\\primer3_config", &st) == 0) && S_ISDIR(st.st_mode)) {
499 thermodynamic_params_path =
500 (char*) malloc(strlen(".\\primer3_config\\") * sizeof(char) + 1);
501 if (NULL == thermodynamic_params_path) exit (-2); /* Out of memory */
502 strcpy(thermodynamic_params_path, ".\\primer3_config\\");
503 } else {
504 /* no default directory found */
505 return;
506 }
507 #else
508 /* in linux, check for ./primer3_config and /opt/primer3_config */
509 struct stat st;
510 if ((stat("./primer3_config", &st) == 0) && S_ISDIR(st.st_mode)) {
511 thermodynamic_params_path =
512 (char*) malloc(strlen("./primer3_config/") * sizeof(char) + 1);
513 if (NULL == thermodynamic_params_path) exit (-2); /* Out of memory */
514 strcpy(thermodynamic_params_path, "./primer3_config/");
515 } else if ((stat("/opt/primer3_config", &st) == 0) && S_ISDIR(st.st_mode)) {
516 thermodynamic_params_path =
517 (char*) malloc(strlen("/opt/primer3_config/") * sizeof(char) + 1);
518 if (NULL == thermodynamic_params_path) exit (-2); /* Out of memory */
519 strcpy(thermodynamic_params_path, "/opt/primer3_config/");
520 } else {
521 /* no default directory found */
522 return;
523 }
524 #endif
525
526 }
527 /* read in the thermodynamic parameters */
528 if (get_thermodynamic_values(thermodynamic_params_path, &o)) {
529 fprintf(stderr, "%s\n", o.msg);
530 exit(-1);
531 }
532 /* mark that the last given path was used for reading the parameters */
533 thermodynamic_path_changed = 0;
534 }
535
536 /* Print out copyright and a short usage message*/
537 static void
print_usage()538 print_usage()
539 {
540 fprintf(stderr, "%s", primer3_copyright());
541
542 fprintf(stderr, "\n\nUSAGE: %s %s %s %s %s %s %s %s %s %s\n", pr_program_name,
543 "[-format_output]",
544 "[-default_version=1|-default_version=2]",
545 "[-io_version=4]",
546 "[-p3_settings_file=<file_path>]",
547 "[-echo_settings_file]",
548 "[-strict_tags]",
549 "[-output=<file_path>]",
550 "[-error=<file_path>]",
551 "[input_file]");
552 fprintf(stderr, "This is primer3 (%s)\n", pr_release);
553 fprintf(stderr, "Input can also be provided on standard input.\n");
554 fprintf(stderr, "For example:\n");
555 fprintf(stderr, "$ primer3_core < my_input_file\n");
556 }
557
558 /* Print out copyright, a short usage message and the signal */
559 static void
sig_handler(int signal)560 sig_handler(int signal)
561 {
562 print_usage();
563 fprintf(stderr, "%s: received signal %d\n", pr_program_name, signal);
564 exit(signal);
565 }
566