comparison srf2fastq/io_lib-1.12.2/progs/convert_trace.c @ 0:d901c9f41a6a default tip

Migrated tool version 1.0.1 from old tool shed archive to new tool shed repository
author dawe
date Tue, 07 Jun 2011 17:48:05 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d901c9f41a6a
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 #include <io_lib/Read.h>
8 #include <io_lib/traceType.h>
9 #include <io_lib/seqIOABI.h>
10 #include <io_lib/open_trace_file.h>
11
12 static char const rcsid[] = "$Id: convert_trace.c,v 1.12 2008-02-20 16:07:44 jkbonfield Exp $";
13
14 #define MAX(a,b) ((a)>(b)?(a):(b))
15
16 struct opts {
17 char *name;
18 char *fofn;
19 char *passed;
20 char *failed;
21 char *error;
22 int in_format;
23 int out_format;
24 int scale;
25 int sub_background;
26 int subtract;
27 int normalise;
28 int min_normalise;
29 int compress_mode;
30 int dots;
31 int noneg;
32 int signed_trace;
33 int skipx;
34 int start;
35 int end;
36 };
37
38 /*
39 * Removes any negative values from a trace by moving each channel
40 * independently so that its lowest value is 0.
41 */
42 void noneg(Read *r) {
43 int i, j, k;
44 signed int min;
45 TRACE *t;
46
47 /* Find the real end of the data */
48 for (k = r->NPoints-1; k >= 0; k--)
49 if (r->traceA[k] ||
50 r->traceC[k] ||
51 r->traceG[k] ||
52 r->traceT[k])
53 break;
54
55 for (j = 0; j < 4; j++) {
56 switch(j) {
57 case 0:
58 t = r->traceA;
59 break;
60 case 1:
61 t = r->traceC;
62 break;
63 case 2:
64 t = r->traceG;
65 break;
66 case 3:
67 default:
68 t = r->traceT;
69 break;
70 }
71
72 /* Find the lowest -ve value per lane */
73 for (min = i = 0; i <= k; i++) {
74 if (min > ((int16_t *)t)[i])
75 min = ((int16_t *)t)[i];
76 }
77
78 /* And shift everything back up */
79 for (i = 0; i <= k; i++) {
80 t[i] -= min;
81 }
82 }
83 }
84
85
86 /*
87 * Removes any negative values from a trace by moving the trace up so that
88 * the lowest overall value is 0. This differs to noneg above by using
89 * a global shift for all channels and also setting the read 'baseline'.
90 */
91 void signed_trace(Read *r) {
92 int i, k;
93 signed int min;
94
95 /* Find the real end of the data */
96 for (k = r->NPoints-1; k >= 0; k--)
97 if (r->traceA[k] ||
98 r->traceC[k] ||
99 r->traceG[k] ||
100 r->traceT[k])
101 break;
102
103 /* Find the lowest -ve value per lane */
104 for (min = i = 0; i <= k; i++) {
105 if (min > ((int16_t *)(r->traceA))[i])
106 min = ((int16_t *)(r->traceA))[i];
107 if (min > ((int16_t *)(r->traceC))[i])
108 min = ((int16_t *)(r->traceC))[i];
109 if (min > ((int16_t *)(r->traceG))[i])
110 min = ((int16_t *)(r->traceG))[i];
111 if (min > ((int16_t *)(r->traceT))[i])
112 min = ((int16_t *)(r->traceT))[i];
113 }
114
115 r->baseline = -min;
116
117 /* And shift everything back up */
118 for (i = 0; i <= k; i++) {
119 r->traceA[i] -= min;
120 r->traceC[i] -= min;
121 r->traceG[i] -= min;
122 r->traceT[i] -= min;
123 }
124 }
125
126 /*
127 * Scales trace values from 0 to scale, but only if they are larger.
128 */
129 void rescale_trace(Read *r, int scale) {
130 double s;
131 int i;
132
133 if (r->maxTraceVal <= scale)
134 return;
135
136 s = ((double)scale)/r->maxTraceVal;
137
138 for (i = 0; i < r->NPoints; i++) {
139 r->traceA[i] = r->traceA[i] * s + 0.5;
140 r->traceC[i] = r->traceC[i] * s + 0.5;
141 r->traceG[i] = r->traceG[i] * s + 0.5;
142 r->traceT[i] = r->traceT[i] * s + 0.5;
143 }
144
145 r->maxTraceVal = scale;
146 }
147
148 #if 0
149 /* OLD method, treats all channels together and assumes the same baseline for
150 * each
151 */
152 /*
153 * Here we just take the minimum trace value and subtract this from all others.
154 * The assumption is that the signal will always be 'base line' on at least
155 * one of the four channels.
156 */
157 void subtract_background(Read *r) {
158 int i, min;
159 for (i = 0; i < r->NPoints; i++) {
160 min = 999999;
161 if (r->traceA[i] < min) min = r->traceA[i];
162 if (r->traceC[i] < min) min = r->traceC[i];
163 if (r->traceG[i] < min) min = r->traceG[i];
164 if (r->traceT[i] < min) min = r->traceT[i];
165 r->traceA[i] -= min;
166 r->traceC[i] -= min;
167 r->traceG[i] -= min;
168 r->traceT[i] -= min;
169 }
170 }
171 #endif
172
173 static void subtract_background_ch(TRACE *channel, int nchannel) {
174 int i, j, bg;
175 int win_len = 501, win_len2 = win_len/2;
176 TRACE *copy;
177
178 if (NULL == (copy = (TRACE *)malloc(sizeof(*copy) * nchannel)))
179 return;
180
181 if (nchannel < win_len)
182 win_len = nchannel;
183
184 /* Take lowest background over win_len and subtract it */
185 for (i = 0; i < nchannel; i++) {
186 /* Could optimise this considerably */
187 bg = INT_MAX;
188 for (j = -win_len2; j < win_len2; j++) {
189 if (i+j < 0) continue;
190 if (i+j >= nchannel) break;
191
192 if (channel[i + j] < bg)
193 bg = channel[i + j];
194 }
195
196 copy[i] = channel[i] - bg;
197 }
198
199 memcpy(channel, copy, nchannel * sizeof(*copy));
200 free(copy);
201 }
202
203 /*
204 * Find the average background level of a trace, and subtract this from the
205 * peak heights.
206 */
207 void subtract_background(Read *r) {
208 subtract_background_ch(r->traceA, r->NPoints);
209 subtract_background_ch(r->traceC, r->NPoints);
210 subtract_background_ch(r->traceG, r->NPoints);
211 subtract_background_ch(r->traceT, r->NPoints);
212 }
213
214 int int_compar(const void *a, const void *b) {
215 return *(const TRACE *)a - *(const TRACE *)b;
216 }
217
218 int find_bg(TRACE *data, int ndata) {
219 int i, bg;
220 TRACE *copy = (TRACE *)malloc(ndata * sizeof(TRACE));
221
222 /* Sort the trace samples by amplitude */
223 memcpy(copy, data, ndata * sizeof(TRACE));
224 qsort(copy, ndata, sizeof(TRACE), int_compar);
225
226 /* Find the first non-zero value */
227 for (i = 0; i < ndata && !copy[i]; i++)
228 ;
229
230 /*
231 * Now take a slie 0.05 through the remainder of the array and set this
232 * as our background.
233 */
234 bg = copy[(int)((ndata - i) * 0.05 + i)];
235
236 free(copy);
237 return bg;
238 }
239
240 void trace_freq(TRACE *data, int ndata) {
241 int i, bg;
242 bg = find_bg(data, ndata);
243
244 for (i = 0; i < ndata; i++) {
245 data[i] = MAX(data[i] - bg, 0);
246 }
247 }
248
249 /*
250 * Separates out the dyes using a deconvolution matrix.
251 * The order of elements in the matrix is C A G T.
252 * A test matrix for the 373. Taken from the BASS distribution.
253 */
254 double matrix[5][4] = {
255 { 0.002439782, -0.0015053751, 0.00011857301, 2.8906948e-06},
256 {-0.00075353298, 0.0032971052, -0.006198165, 0.00014828549},
257 { 0.00020249287, -0.0017620348, 0.010530438, -0.0020235507 },
258 {-0.001144423, -4.857673e-06, -0.0018845701, 0.00395431 },
259 {-0.12451385, 0.368916, -2.928292, -3.3142638 }
260 };
261 void separate_dyes(Read *r, double M[][4]) {
262 int i, j;
263
264 for (i = 0; i < r->NPoints; i++) {
265 int C, A, G, T;
266 double sep[4];
267
268 C = r->traceC[i];
269 A = r->traceA[i];
270 G = r->traceG[i];
271 T = r->traceT[i];
272
273 for (j = 0; j < 4; j++)
274 sep[j] = C*M[0][j] + A*M[1][j] + G*M[2][j] + T*M[3][j] + M[4][j];
275
276 for (j = 0; j < 4; j++)
277 sep[j] += 10;
278
279 /* hack!
280 sep[0] += 0.1;
281 sep[1] += -0.4;
282 sep[2] += 2.9;
283 sep[3] += 3.2;
284 */
285
286 r->traceC[i] = sep[0] < 0 ? 0 : 1000 * sep[0];
287 r->traceA[i] = sep[1] < 0 ? 0 : 1000 * sep[1];
288 r->traceG[i] = sep[2] < 0 ? 0 : 1000 * sep[2];
289 r->traceT[i] = sep[3] < 0 ? 0 : 1000 * sep[3];
290 }
291 }
292
293 /*
294 * Find the maximum height of traces at the called bases. Use this to clip any
295 * other bases.
296 */
297 void reset_max_called_height(Read *r) {
298 int i, max = 0;
299
300 /* Find max */
301 for (i=0; i < r->NBases; i++) {
302 switch(r->base[i]) {
303 case 'a':
304 case 'A':
305 if (r->traceA[r->basePos[i]] > max)
306 max = r->traceA[r->basePos[i]];
307 break;
308
309 case 'c':
310 case 'C':
311 if (r->traceC[r->basePos[i]] > max)
312 max = r->traceC[r->basePos[i]];
313 break;
314
315 case 'g':
316 case 'G':
317 if (r->traceG[r->basePos[i]] > max)
318 max = r->traceG[r->basePos[i]];
319 break;
320
321 case 't':
322 case 'T':
323 if (r->traceT[r->basePos[i]] > max)
324 max = r->traceT[r->basePos[i]];
325 break;
326 }
327 }
328
329 /* Clip to max */
330 for (i = 0; i < r->NPoints; i++) {
331 if (r->traceA[i] > max)
332 r->traceA[i] = max;
333 if (r->traceC[i] > max)
334 r->traceC[i] = max;
335 if (r->traceG[i] > max)
336 r->traceG[i] = max;
337 if (r->traceT[i] > max)
338 r->traceT[i] = max;
339 }
340 if (r->maxTraceVal > max)
341 r->maxTraceVal = max;
342 }
343
344 /*
345 * Rescales peak heights based on a moving "marker". The marker tracks
346 * up and down (attack and decay) based on the difference between itself and
347 * the trace envelope. We then divide by the marker value to attempt to
348 * normalise peak heights.
349 *
350 * min_marker is used to avoid scaling up noise and represents the minimum
351 * value the marker is allowed to reach. Make sure it is > 0 or divide by
352 * zero may occur.
353 */
354 void rescale_heights(Read *r, int min_marker) {
355 double marker = 0;
356 int i, j, max, mtv = 0;
357 TRACE *tx[4];
358
359 tx[0] = r->traceA;
360 tx[1] = r->traceC;
361 tx[2] = r->traceG;
362 tx[3] = r->traceT;
363
364 for (i = 0; i < r->NPoints; i++) {
365 for (max = j = 0; j < 4; j++)
366 if (max < tx[j][i])
367 max = tx[j][i];
368 if (!marker) {
369 marker = max;
370 } else {
371 if (max >= marker) {
372 /* attack */
373 marker += (max - marker) / 20.0;
374 } else {
375 /* decay */
376 marker -= (marker - max) / 10.0;
377 }
378 }
379 if (marker < min_marker)
380 marker = min_marker;
381
382 for (j = 0; j < 4; j++) {
383 double new = tx[j][i] * 2000.0/marker;
384 tx[j][i] = new > 32767 ? 32767 : new;
385 if (mtv < tx[j][i])
386 mtv = tx[j][i];
387 }
388
389 }
390
391 r->maxTraceVal = mtv;
392 }
393
394 /* Removes every other sample as a crude way to reduce file size */
395 void skipx(Read *r) {
396 int i, j;
397 for (i = j = 0; j < r->NPoints/2; i+=2, j++) {
398 r->traceA[j] = (r->traceA[i] + r->traceA[i+1]) / 2;
399 r->traceC[j] = (r->traceC[i] + r->traceC[i+1]) / 2;
400 r->traceG[j] = (r->traceG[i] + r->traceG[i+1]) / 2;
401 r->traceT[j] = (r->traceT[i] + r->traceT[i+1]) / 2;
402 }
403 r->NPoints = j;
404
405 for (i = 0; i < r->NBases; i++) {
406 r->basePos[i] /= 2;
407 }
408 }
409
410 void clip_range(Read *r, int left, int right) {
411 int i, j;
412 if (left != -1) {
413 for (i = 0, j = left; j < r->NPoints; i++, j++) {
414 r->traceA[i] = r->traceA[j];
415 r->traceC[i] = r->traceC[j];
416 r->traceG[i] = r->traceG[j];
417 r->traceT[i] = r->traceT[j];
418 }
419 right -= left;
420 }
421 if (right > 0) {
422 r->NPoints = right;
423 }
424 }
425
426
427 int convert(mFILE *infp, mFILE *outfp, char *infname, char *outfname,
428 struct opts *opts) {
429 Read *r;
430
431 if (NULL == (r = mfread_reading(infp, infname, opts->in_format))) {
432 fprintf(stderr, "failed to read file %s\n", infname);
433 return 1;
434 }
435
436 if (opts->start != -1 || opts->end != -1)
437 clip_range(r, opts->start, opts->end);
438
439 if (opts->skipx) {
440 skipx(r);
441 }
442
443 if (opts->noneg)
444 noneg(r);
445
446 if (opts->signed_trace)
447 signed_trace(r);
448
449 if (opts->subtract) {
450 int i;
451 for (i = 0; i < r->NPoints; i++) {
452 r->traceA[i] = MAX(0, r->traceA[i] - opts->subtract);
453 r->traceC[i] = MAX(0, r->traceC[i] - opts->subtract);
454 r->traceG[i] = MAX(0, r->traceG[i] - opts->subtract);
455 r->traceT[i] = MAX(0, r->traceT[i] - opts->subtract);
456 }
457 }
458
459 if (opts->sub_background) {
460 /*
461 trace_freq(r->traceA, r->NPoints);
462 trace_freq(r->traceC, r->NPoints);
463 trace_freq(r->traceG, r->NPoints);
464 trace_freq(r->traceT, r->NPoints);
465 */
466 subtract_background(r);
467 /*
468 separate_dyes(r, matrix);
469 trace_freq(r->traceA, r->NPoints);
470 trace_freq(r->traceC, r->NPoints);
471 trace_freq(r->traceG, r->NPoints);
472 trace_freq(r->traceT, r->NPoints);
473 */
474 reset_max_called_height(r);
475 }
476
477 if (opts->normalise) {
478 rescale_heights(r, opts->min_normalise);
479 }
480
481 if (opts->scale) {
482 rescale_trace(r, opts->scale);
483 }
484
485 if (opts->name)
486 r->ident = strdup(opts->name);
487 else if (0 == strcmp(outfname, "(stdout)"))
488 r->ident = strdup(infname);
489 else
490 r->ident = strdup(outfname);
491
492 if (opts->compress_mode != -1)
493 set_compression_method(opts->compress_mode);
494
495 if (0 != (mfwrite_reading(outfp, r, opts->out_format))) {
496 fprintf(stderr, "failed to write file %s\n", outfname);
497 read_deallocate(r);
498 return 1;
499 }
500
501 read_deallocate(r);
502 return 0;
503 }
504
505
506 void usage(void) {
507 puts("Usage: convert_trace [options] [informat outformat] < in > out");
508 puts("Or convert_trace [options] -fofn file_of_filenames");
509 puts("\nOptions are:");
510 puts(" -in_format format Format for input (defaults to any");
511 puts(" -out_format format Format for output (default ztr)");
512 puts(" -fofn file_of_filenames Get \"Input Output\" names from a fofn");
513 puts(" -passed fofn Output fofn of passed names");
514 puts(" -error errs Redirect stderr to file \"errs\"");
515 puts(" -failed fofn Output fofn of failed names");
516 puts(" -name id ID line for experiment file output");
517 puts(" -subtract_background Auto-subtracts the trace background");
518 puts(" -subtract amount Subtracts a specified background amount");
519 puts(" -normalise Normalises peak heights");
520 puts(" -min_normalise Minimum trace amp for normalising");
521 puts(" -scale range Downscales peaks to 0-range");
522 puts(" -compress mode Compress file output (not if stdout)");
523 puts(" -abi_data counts ABI DATA lanes to copy: eg 9,10,11,12");
524 puts(" -signed Apply global shift to avoid negative values");
525 puts(" -noneg Shift each channel independently to avoid -ve");
526 puts(" -- Explicitly state end of options");
527 exit(1);
528 }
529
530 int main(int argc, char **argv) {
531 struct opts opts;
532
533 opts.in_format = TT_ANY;
534 opts.out_format = TT_ZTR;
535 opts.scale = 0;
536 opts.sub_background = 0;
537 opts.subtract = 0;
538 opts.normalise = 0;
539 opts.min_normalise = 100;
540 opts.name = NULL;
541 opts.compress_mode = -1;
542 opts.dots = 0;
543 opts.noneg = 0;
544 opts.signed_trace = 0;
545 opts.fofn = NULL;
546 opts.passed = NULL;
547 opts.failed = NULL;
548 opts.error = NULL;
549 opts.skipx = 0;
550 opts.start = -1;
551 opts.end = -1;
552
553 for (argc--, argv++; argc > 0; argc--, argv++) {
554 if (**argv != '-')
555 break;
556
557 if (strcmp(*argv, "-start") == 0) {
558 opts.start = atoi(*++argv);
559 argc--;
560
561 } else if (strcmp(*argv, "-end") == 0) {
562 opts.end = atoi(*++argv);
563 argc--;
564
565 } else if (strcmp(*argv, "-scale") == 0) {
566 opts.scale = atoi(*++argv);
567 argc--;
568
569 } else if (strcmp(*argv, "-fofn") == 0) {
570 opts.fofn = *++argv;
571 argc--;
572
573 } else if (strcmp(*argv, "-passed") == 0) {
574 opts.passed = *++argv;
575 argc--;
576
577 } else if (strcmp(*argv, "-failed") == 0) {
578 opts.failed = *++argv;
579 argc--;
580
581 } else if (strcmp(*argv, "-error") == 0) {
582 opts.error = *++argv;
583 argc--;
584
585 } else if (strcmp(*argv, "-subtract_background") == 0) {
586 opts.sub_background = 1;
587
588 } else if (strcmp(*argv, "-subtract") == 0) {
589 opts.subtract = atoi(*++argv);
590 argc--;
591
592 } else if (strcmp(*argv, "-normalise") == 0) {
593 opts.normalise = 1;
594
595 } else if (strcmp(*argv, "-min_normalise") == 0) {
596 opts.min_normalise = atoi(*++argv);
597 argc--;
598
599 } else if (strcmp(*argv, "-dots") == 0) {
600 opts.dots = 1;
601
602 } else if (strcmp(*argv, "-noneg") == 0) {
603 opts.noneg = 1;
604
605 } else if (strcmp(*argv, "-signed") == 0) {
606 opts.signed_trace = 1;
607
608 } else if (strcmp(*argv, "-skipx") == 0) {
609 opts.skipx = 1;
610
611 } else if (strcmp(*argv, "-in_format") == 0) {
612 argv++;
613 argc--;
614 if (TT_UNK == (opts.in_format = trace_type_str2int(*argv)))
615 opts.in_format = atoi(*argv);
616
617 } else if (strcmp(*argv, "-name") == 0) {
618 opts.name = *++argv;
619 argc--;
620
621 } else if (strcmp(*argv, "-out_format") == 0) {
622 argv++;
623 argc--;
624 if (TT_UNK == (opts.out_format = trace_type_str2int(*argv)))
625 opts.out_format = atoi(*argv);
626
627 } else if (strcmp(*argv, "-compress") == 0) {
628 opts.compress_mode = compress_str2int(*++argv);
629 argc--;
630
631 } else if (strcmp(*argv, "-abi_data") == 0) {
632 int c1, c2, c3, c4;
633 argc--;
634 if (4 == sscanf(*++argv, "%d,%d,%d,%d", &c1, &c2, &c3, &c4)) {
635 abi_set_data_counts(c1, c2, c3, c4);
636 } else {
637 usage();
638 }
639
640 } else if (strcmp(*argv, "--") == 0) {
641 break;
642
643 } else {
644 usage();
645 }
646 }
647
648 if (argc == 2) {
649 /* Old syntax, for backwards compatibility */
650
651 if (TT_UNK == (opts.in_format = trace_type_str2int(argv[0])))
652 opts.in_format = atoi(argv[0]);
653 if (TT_UNK == (opts.out_format = trace_type_str2int(argv[1])))
654 opts.out_format = atoi(argv[1]);
655 } else if (argc != 0) {
656 usage();
657 }
658
659
660 /*
661 * Added by SAK: Allow redirection of error output to file, due to
662 * problems with Java exec
663 */
664 if (NULL != opts.error) {
665 int fd;
666
667 fprintf(stderr,"* Redirecting stderr to %s\n", opts.error);
668
669 close(2); /* close fd with stderr */
670 if (-1 == (fd = creat(opts.error, 0666))) {
671 exit(1);
672 }
673 }
674
675 if (!opts.fofn) {
676 return convert(mstdin(), mstdout(), "(stdin)", "(stdout)", &opts);
677 }
678
679 /* else */ {
680 mFILE *fpin, *fpout;
681 FILE *fppassed = NULL, *fpfailed = NULL;
682 char *infname, *outfname;
683 int ret, ret_all = 0;
684 char line[8192], line2[8192];
685
686 FILE *fofn_fp;
687
688 if (NULL == (fofn_fp = fopen(opts.fofn, "r"))) {
689 perror(opts.fofn);
690 return -1;
691 }
692
693 if (opts.passed && NULL == (fppassed = fopen(opts.passed, "w"))) {
694 perror(opts.passed);
695 return -1;
696 }
697
698 if (opts.failed && NULL == (fpfailed = fopen(opts.failed, "w"))) {
699 perror(opts.failed);
700 return -1;
701 }
702
703 while (fgets(line, 8192, fofn_fp) != NULL) {
704 int i, j, len;
705
706 /* Find input and output name, escaping spaces as needed */
707 len = strlen(line);
708 outfname = NULL;
709 for (i = j = 0; i < len; i++) {
710 if (line[i] == '\\' && i != len-1) {
711 line2[j++] = line[++i];
712 } else if (line[i] == ' ') {
713 line2[j++] = 0;
714 outfname = &line2[j];
715 } else if (line[i] != '\n') {
716 line2[j++] = line[i];
717 }
718 }
719 line2[j] = 0;
720 infname = line2;
721
722 /* Don't clobber input */
723 if (!strcmp(infname, outfname)) {
724 fprintf(stderr,"* Inputfn %s == Outputfn %s ...skipping\n",
725 infname, outfname);
726 if (fpfailed)
727 fprintf(fpfailed, "%s\n", infname);
728 continue;
729 }
730
731 /* Open input and output files */
732 if (opts.in_format == TT_EXP) {
733 fpin = open_exp_mfile(infname, NULL);
734 } else {
735 fpin = open_trace_mfile(infname, NULL);
736 }
737 if (NULL == fpin) {
738 char buf[2048];
739 sprintf(buf, "ERROR %s", infname);
740 perror(buf);
741 if (opts.dots) {
742 fputc('!', stdout);
743 fflush(stdout);
744 }
745 if (fpfailed)
746 fprintf(fpfailed, "%s\n", infname);
747 continue;
748 }
749
750 if (outfname) {
751 if (NULL == (fpout = mfopen(outfname, "wb+"))) {
752 char buf[2048];
753 sprintf(buf, "ERROR %s", outfname);
754 perror(buf);
755 mfclose(fpin);
756 if (opts.dots) {
757 fputc('!', stdout);
758 fflush(stdout);
759 }
760 if (fpfailed)
761 fprintf(fpfailed, "%s\n", infname);
762 continue;
763 }
764 } else {
765 outfname = "(stdout)";
766 fpout = mstdout();
767 }
768
769 /* Convert */
770 ret = convert(fpin, fpout, infname, outfname, &opts);
771 ret_all |= ret;
772 if (opts.dots) {
773 fputc(ret ? '!' : '.', stdout);
774 fflush(stdout);
775 }
776 if (ret) {
777 if (fpfailed)
778 fprintf(fpfailed, "%s\n", infname);
779 } else {
780 if (fppassed)
781 fprintf(fppassed, "%s\n", infname);
782 }
783
784 /* Tidy up */
785 mfclose(fpin);
786 if (fpout != mstdout())
787 mfclose(fpout);
788 }
789
790 fclose(fofn_fp);
791
792 return ret_all;
793 }
794 }