]> git.donarmstrong.com Git - fastq-tools.git/blob - src/parse.c
a program to output a nonredundant list of readss
[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")),
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     int 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         else if (*f->c == ';') {
173             fastq_get_line(f, NULL);
174             if (f->state == STATE_EOF) return 0;
175         }
176
177         /* read id1 */
178         else if (f->state == STATE_ID1) {
179             if (*f->c == '@') {
180                 f->c++;
181                 fastq_get_line(f, &seq->id1);
182                 if (f->state == STATE_EOF) return 0;
183
184                 f->state = STATE_SEQ;
185             }
186             else {
187                 fprintf(stderr, "Malformed FASTQ file: expecting an '@', saw a '%c'\n", *f->c);
188                 exit(1);
189             }
190         }
191
192         /* read sequence */
193         else if (f->state == STATE_SEQ) {
194             fastq_get_line(f, &seq->seq);
195             if (f->state == STATE_EOF) return 0;
196
197             f->state = STATE_ID2;
198         }
199
200         /* read id2 */
201         else if (f->state == STATE_ID2) {
202             if (*f->c == '+') {
203                 f->c++;
204                 fastq_get_line(f, &seq->id2);
205                 if (f->state == STATE_EOF) return 0;
206
207                 f->state = STATE_QUAL;
208             }
209             else {
210                 fprintf(stderr, "Malformed FASTQ file: expecting an '+', saw a '%c'\n", *f->c);
211                 exit(1);
212             }
213         }
214
215         /* read quality string */
216         else if (f->state == STATE_QUAL) {
217             fastq_get_line(f, &seq->qual);
218             if (f->state == STATE_EOF) return 1;
219
220             f->state = STATE_ID1;
221             break;
222         }
223
224         else {
225             fputs("Inexplicable error in fastq parser.\n", stderr);
226             exit(1);
227         }
228
229         f->c++;
230     }
231
232     return 1;
233 }
234
235