]> git.donarmstrong.com Git - fastq-tools.git/blob - src/parse.c
A program to randomly sample reads from a fastq file.
[fastq-tools.git] / src / parse.c
1 /*
2  * This file is part of fastq-tools.
3  *
4  * Copyright (c) 2011 by Daniel C. Jones <dcjones@cs.washington.edu>
5  *
6  */
7
8 #include "parse.h"
9 #include "common.h"
10 #include <stdlib.h>
11 #include <ctype.h>
12
13
14 static const size_t init_str_size  = 128;
15 static const size_t fastq_buf_size = 4096;
16
17 static void fastq_alloc_str(str_t* s)
18 {
19     s->s = malloc_or_die(init_str_size);
20     s->s[0] = '\0';
21     s->n = 0;
22     s->size = init_str_size;
23 }
24
25
26 static void fastq_expand_str(str_t* s)
27 {
28     s->size *= 2;
29     realloc_or_die(s->s, s->size);
30 }
31
32
33 seq_t* fastq_alloc_seq()
34 {
35     seq_t* seq = malloc_or_die(sizeof(seq_t));
36     fastq_alloc_str(&seq->id1);
37     fastq_alloc_str(&seq->seq);
38     fastq_alloc_str(&seq->id2);
39     fastq_alloc_str(&seq->qual);
40
41     return seq;
42 }
43
44
45 void fastq_free_seq(seq_t* seq)
46 {
47     free(seq->id1.s);
48     free(seq->seq.s);
49     free(seq->id2.s);
50     free(seq->qual.s);
51     free(seq);
52 }
53
54
55 typedef enum
56 {
57     STATE_EOF,
58     STATE_ERROR,
59     STATE_ID1,
60     STATE_SEQ,
61     STATE_ID2,
62     STATE_QUAL
63
64 } fastq_state;
65
66
67 fastq_t* fastq_open(FILE* f)
68 {
69     fastq_t* fqf = malloc_or_die(sizeof(fastq_t));
70     or_die((int)((fqf->file = gzdopen(fileno(f), "rb")) != NULL),
71            "Can not open gzip file.");
72     
73     fqf->state = STATE_ID1;
74     fqf->buf = malloc_or_die(fastq_buf_size);
75     fqf->buf[0] = '\0';
76     fqf->c = fqf->buf;
77
78     return fqf;
79 }
80
81
82 void fastq_close(fastq_t* fqf)
83 {
84     gzclose(fqf->file);
85     free(fqf->buf);
86     free(fqf);
87 }
88
89
90 void fastq_refill(fastq_t* f)
91 {
92     int errnum;
93     const char* errmsg;
94
95     int n = gzread(f->file, f->buf, fastq_buf_size - 1);
96
97     if (n <= 0) {
98         if (gzeof(f->file)) {
99             f->state = STATE_EOF;
100             n = 0;
101         }
102         else {
103             errmsg = gzerror(f->file, &errnum);
104             fprintf(stderr, "I/O error: %s\n", errmsg);
105             exit(1);
106         }
107     }
108
109     f->buf[n] = '\0';
110     f->c = f->buf;
111 }
112
113
114 void fastq_get_line(fastq_t* f, str_t* s)
115 {
116     size_t i = 0;
117
118     if (f->state == STATE_EOF) goto fastq_get_line_done;
119
120     while (1) {
121         switch (*f->c) {
122             case '\0':
123                 fastq_refill(f);
124                 if (f->state == STATE_EOF) goto fastq_get_line_done;
125                 break;
126
127             case '\r':
128                 f->c++;
129                 break;
130
131             case '\n':
132                 goto fastq_get_line_done;
133
134             default:
135                 while (s->size < i + 2) {
136                     fastq_expand_str(s);
137                 }
138                 if (s) s->s[i++] = *f->c;
139                 f->c++;
140         }
141
142     }
143
144 fastq_get_line_done:
145     if (s) {
146         s->s[i] = '\0';
147         s->n = i;
148     }
149 }
150
151
152
153 int fastq_next(fastq_t* f, seq_t* seq)
154 {
155     if (f->state == STATE_EOF) return 0;
156
157     while (1) {
158
159         /* read more, if needed */
160         if (*f->c == '\0' ) {
161             fastq_refill(f);
162             if (f->state == STATE_EOF) return 0;
163             continue;
164         }
165
166         /* skip over leading whitespace */
167         else if (isspace(*f->c)) {
168             /* do nothing */
169         }
170
171         /* skip comments */
172         /*
173         else if (*f->c == ';') {
174             fastq_get_line(f, NULL);
175             if (f->state == STATE_EOF) return 0;
176         }
177         */
178
179         /* read id1 */
180         else if (f->state == STATE_ID1) {
181             if (*f->c == '@' || *f->c == '>') {
182                 f->c++;
183                 fastq_get_line(f, &seq->id1);
184                 if (f->state == STATE_EOF) return 0;
185
186                 f->state = STATE_SEQ;
187             }
188             else {
189                 fprintf(stderr,
190                         "Malformed FASTQ file: expecting an '@' or '>', saw a '%c'\n",
191                         *f->c);
192                 exit(1);
193             }
194         }
195
196         /* read sequence */
197         else if (f->state == STATE_SEQ) {
198             fastq_get_line(f, &seq->seq);
199             if (f->state == STATE_EOF) return 0;
200
201             f->state = STATE_ID2;
202         }
203
204         /* read id2 */
205         else if (f->state == STATE_ID2) {
206             if (*f->c == '+') {
207                 f->c++;
208                 fastq_get_line(f, &seq->id2);
209                 if (f->state == STATE_EOF) return 0;
210
211                 f->state = STATE_QUAL;
212             }
213             else {
214                 /* fasta style entry */
215                 seq->id2.s[0]  = '\0';
216                 seq->qual.s[0] = '\0';
217
218                 f->state = STATE_ID1;
219                 break;
220             }
221         }
222
223         /* read quality string */
224         else if (f->state == STATE_QUAL) {
225             fastq_get_line(f, &seq->qual);
226             if (f->state == STATE_EOF) return 1;
227
228             f->state = STATE_ID1;
229             break;
230         }
231
232         else {
233             fputs("Inexplicable error in fastq parser.\n", stderr);
234             exit(1);
235         }
236
237         f->c++;
238     }
239
240     return 1;
241 }
242
243
244 void fastq_rewind(fastq_t* fqf)
245 {
246     gzrewind(fqf->file);
247     fqf->state = STATE_ID1;
248     fqf->buf[0] = '\0';
249     fqf->c = fqf->buf;
250 }
251
252
253 void fastq_print(FILE* fout, seq_t* seq)
254 {
255     /* FASTQ */
256     if (seq->qual.n > 0) {
257         fprintf(fout, "@%s\n%s\n+%s\n%s\n",
258                       seq->id1.s,
259                       seq->seq.s,
260                       seq->id2.s,
261                       seq->qual.s );
262     }
263
264     /* FASTA */
265     else {
266         fprintf(fout, ">%s\n%s\n",
267                       seq->id1.s,
268                       seq->seq.s );
269     }
270 }
271
272