1 /*  test/hfile.c -- Test cases for low-level input/output streams.
2 
3     Copyright (C) 2013-2014, 2016, 2018 Genome Research Ltd.
4 
5     Author: John Marshall <jm18@sanger.ac.uk>
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 DEALINGS IN THE SOFTWARE.  */
24 
25 #include <config.h>
26 
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 
32 #include <sys/stat.h>
33 
34 #include "../htslib/hfile.h"
35 #include "../htslib/hts_defs.h"
36 #include "../htslib/kstring.h"
37 
fail(const char * format,...)38 void HTS_NORETURN fail(const char *format, ...)
39 {
40     int err = errno;
41     va_list args;
42     va_start(args, format);
43     vfprintf(stderr, format, args);
44     va_end(args);
45     if (err != 0) fprintf(stderr, ": %s", strerror(err));
46     fprintf(stderr, "\n");
47     exit(EXIT_FAILURE);
48 }
49 
check_offset(hFILE * f,off_t off,const char * message)50 void check_offset(hFILE *f, off_t off, const char *message)
51 {
52     off_t ret = htell(f);
53     if (ret < 0) fail("htell(%s)", message);
54     if (ret == off) return;
55 
56     fprintf(stderr, "%s offset incorrect: expected %ld but got %ld\n",
57             message, (long)off, (long)ret);
58     exit(EXIT_FAILURE);
59 }
60 
slurp(const char * filename)61 char *slurp(const char *filename)
62 {
63     char *text;
64     struct stat sbuf;
65     size_t filesize;
66     FILE *f = fopen(filename, "rb");
67     if (f == NULL) fail("fopen(\"%s\", \"rb\")", filename);
68     if (fstat(fileno(f), &sbuf) != 0) fail("fstat(\"%s\")", filename);
69     filesize = sbuf.st_size;
70 
71     text = (char *) malloc(filesize + 1);
72     if (text == NULL) fail("malloc(text)");
73 
74     if (fread(text, 1, filesize, f) != filesize) fail("fread");
75     fclose(f);
76 
77     text[filesize] = '\0';
78     return text;
79 }
80 
81 hFILE *fin = NULL;
82 hFILE *fout = NULL;
83 
reopen(const char * infname,const char * outfname)84 void reopen(const char *infname, const char *outfname)
85 {
86     if (fin) { if (hclose(fin) != 0) fail("hclose(input)"); }
87     if (fout) { if (hclose(fout) != 0) fail("hclose(output)"); }
88 
89     fin = hopen(infname, "r");
90     if (fin == NULL) fail("hopen(\"%s\")", infname);
91 
92     fout = hopen(outfname, "w");
93     if (fout == NULL) fail("hopen(\"%s\")", outfname);
94 }
95 
main(void)96 int main(void)
97 {
98     static const int size[] = { 1, 13, 403, 999, 30000 };
99 
100     char buffer[40000];
101     char *original;
102     int c, i;
103     ssize_t n;
104     off_t off;
105 
106     reopen("vcf.c", "test/hfile1.tmp");
107     while ((c = hgetc(fin)) != EOF) {
108         if (hputc(c, fout) == EOF) fail("hputc");
109     }
110     if (herrno(fin)) { errno = herrno(fin); fail("hgetc"); }
111 
112     reopen("test/hfile1.tmp", "test/hfile2.tmp");
113     if (hpeek(fin, buffer, 50) < 0) fail("hpeek");
114     while ((n = hread(fin, buffer, 17)) > 0) {
115         if (hwrite(fout, buffer, n) != n) fail("hwrite");
116     }
117     if (n < 0) fail("hread");
118 
119     reopen("test/hfile2.tmp", "test/hfile3.tmp");
120     while ((n = hread(fin, buffer, sizeof buffer)) > 0) {
121         if (hwrite(fout, buffer, n) != n) fail("hwrite");
122         if (hpeek(fin, buffer, 700) < 0) fail("hpeek");
123     }
124     if (n < 0) fail("hread");
125 
126     reopen("test/hfile3.tmp", "test/hfile4.tmp");
127     i = 0;
128     off = 0;
129     while ((n = hread(fin, buffer, size[i++ % 5])) > 0) {
130         off += n;
131         buffer[n] = '\0';
132         check_offset(fin, off, "pre-peek");
133         if (hputs(buffer, fout) == EOF) fail("hputs");
134         if ((n = hpeek(fin, buffer, size[(i+3) % 5])) < 0) fail("hpeek");
135         check_offset(fin, off, "post-peek");
136     }
137     if (n < 0) fail("hread");
138 
139     reopen("test/hfile4.tmp", "test/hfile5.tmp");
140     while (hgets(buffer, 80, fin) != NULL) {
141         size_t l = strlen(buffer);
142         if (l > 79) fail("hgets read %zu bytes, should be < 80", l);
143         if (hwrite(fout, buffer, l) != l) fail("hwrite");
144     }
145     if (herrno(fin)) fail("hgets");
146 
147     reopen("test/hfile5.tmp", "test/hfile6.tmp");
148     n = hread(fin, buffer, 200);
149     if (n < 0) fail("hread");
150     else if (n != 200) fail("hread only got %d", (int)n);
151     if (hwrite(fout, buffer, 1000) != 1000) fail("hwrite");
152     check_offset(fin, 200, "input/first200");
153     check_offset(fout, 1000, "output/first200");
154 
155     if (hseek(fin, 800, SEEK_CUR) < 0) fail("hseek/cur");
156     check_offset(fin, 1000, "input/seek");
157     for (off = 1000; (n = hread(fin, buffer, sizeof buffer)) > 0; off += n)
158         if (hwrite(fout, buffer, n) != n) fail("hwrite");
159     if (n < 0) fail("hread");
160     check_offset(fin, off, "input/eof");
161     check_offset(fout, off, "output/eof");
162 
163     if (hseek(fin, 200, SEEK_SET) < 0) fail("hseek/set");
164     if (hseek(fout, 200, SEEK_SET) < 0) fail("hseek(output)");
165     check_offset(fin, 200, "input/backto200");
166     check_offset(fout, 200, "output/backto200");
167     n = hread(fin, buffer, 800);
168     if (n < 0) fail("hread");
169     else if (n != 800) fail("hread only got %d", (int)n);
170     if (hwrite(fout, buffer, 800) != 800) fail("hwrite");
171     check_offset(fin, 1000, "input/wrote800");
172     check_offset(fout, 1000, "output/wrote800");
173 
174     if (hflush(fout) == EOF) fail("hflush");
175 
176     original = slurp("vcf.c");
177     for (i = 1; i <= 6; i++) {
178         char *text;
179         sprintf(buffer, "test/hfile%d.tmp", i);
180         text = slurp(buffer);
181         if (strcmp(original, text) != 0) {
182             fprintf(stderr, "%s differs from vcf.c\n", buffer);
183             free(text);
184             free(original);
185             return EXIT_FAILURE;
186         }
187         free(text);
188     }
189     free(original);
190 
191     if (hclose(fin) != 0) fail("hclose(input)");
192     if (hclose(fout) != 0) fail("hclose(output)");
193 
194     fout = hopen("test/hfile_chars.tmp", "w");
195     if (fout == NULL) fail("hopen(\"test/hfile_chars.tmp\")");
196     for (i = 0; i < 256; i++)
197         if (hputc(i, fout) != i) fail("chars: hputc (%d)", i);
198     if (hclose(fout) != 0) fail("hclose(test/hfile_chars.tmp)");
199 
200     fin = hopen("test/hfile_chars.tmp", "r");
201     if (fin == NULL) fail("hopen(\"test/hfile_chars.tmp\") for reading");
202     for (i = 0; i < 256; i++)
203         if ((c = hgetc(fin)) != i)
204             fail("chars: hgetc (%d = 0x%x) returned %d = 0x%x", i, i, c, c);
205     if ((c = hgetc(fin)) != EOF) fail("chars: hgetc (EOF) returned %d", c);
206     if (hclose(fin) != 0) fail("hclose(test/hfile_chars.tmp) for reading");
207 
208     fin = hopen("preload:test/hfile_chars.tmp", "r");
209     if (fin == NULL) fail("preloading \"test/hfile_chars.tmp\" for reading");
210     for (i = 0; i < 256; i++)
211         if ((c = hgetc(fin)) != i)
212             fail("preloading chars: hgetc (%d = 0x%x) returned %d = 0x%x", i, i, c, c);
213     if ((c = hgetc(fin)) != EOF) fail("preloading chars: hgetc (EOF) returned %d", c);
214     if (hclose(fin) != 0) fail("preloading hclose(test/hfile_chars.tmp) for reading");
215 
216     char* test_string = strdup("Test string");
217     fin = hopen("mem:", "r:", test_string, 12);
218     if (fin == NULL) fail("hopen(\"mem:\", \"r:\", ...)");
219     if (hread(fin, buffer, 12) != 12)
220         fail("hopen('mem:', 'r') failed read");
221     if(strcmp(buffer, test_string) != 0)
222         fail("hopen('mem:', 'r') missread '%s' != '%s'", buffer, test_string);
223     char* internal_buf;
224     size_t interval_buf_len;
225     if((internal_buf = hfile_mem_get_buffer(fin, &interval_buf_len)) == NULL){
226         fail("hopen('mem:', 'r') failed to get internal buffer");
227     }
228     if (hclose(fin) != 0) fail("hclose mem for reading");
229 
230     test_string = strdup("Test string");
231     fin = hopen("mem:", "wr:", test_string, 12);
232     if (fin == NULL) fail("hopen(\"mem:\", \"w:\", ...)");
233     if (hseek(fin, -1, SEEK_END) < 0)
234         fail("hopen('mem:', 'wr') failed seek");
235     if (hwrite(fin, " extra", 7) != 7)
236         fail("hopen('mem:', 'wr') failed write");
237     if (hseek(fin, 0, SEEK_SET) < 0)
238         fail("hopen('mem:', 'wr') failed seek");
239     if (hread(fin, buffer, 18) != 18)
240         fail("hopen('mem:', 'wr') failed read");
241     if (strcmp(buffer, "Test string extra") != 0)
242         fail("hopen('mem:', 'wr') misswrote '%s' != '%s'", buffer, "Test string extra");
243     if((internal_buf = hfile_mem_steal_buffer(fin, &interval_buf_len)) == NULL){
244         fail("hopen('mem:', 'wr') failed to get internal buffer");
245     }
246     free(internal_buf);
247     if (hclose(fin) != 0) fail("hclose mem for writing");
248 
249     fin = hopen("data:,hello, world!%0A", "r");
250     if (fin == NULL) fail("hopen(\"data:...\")");
251     n = hread(fin, buffer, 300);
252     if (n < 0) fail("hread");
253     buffer[n] = '\0';
254     if (strcmp(buffer, "hello, world!\x0A") != 0) fail("hread result");
255     if (hclose(fin) != 0) fail("hclose(\"data:...\")");
256 
257     fin = hopen("test/emptyfile", "r");
258     if (fin == NULL) fail("hopen(\"test/emptyfile\") for reading");
259     if (hread(fin, buffer, 100) != 0) fail("test/emptyfile is non-empty");
260     if (hclose(fin) != 0) fail("hclose(\"test/emptyfile\") for reading");
261 
262     fin = hopen("data:,", "r");
263     if (fin == NULL) fail("hopen(\"data:\") for reading");
264     if (hread(fin, buffer, 100) != 0) fail("empty data: URL is non-empty");
265     if (hclose(fin) != 0) fail("hclose(\"data:\") for reading");
266 
267     fin = hopen("data:;base64,"
268 // Wikipedia's example quote from Thomas Hobbes' Leviathan
269 "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
270 "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
271 "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
272 "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
273 "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", "r");
274     if (fin == NULL) fail("hopen(\"data:;base64,...\")");
275     n = hread(fin, buffer, 300);
276     if (n < 0) fail("hread for base64");
277     buffer[n] = '\0';
278     if (strcmp(buffer, "Man is distinguished, not only by his reason, but by "
279 "this singular passion from other animals, which is a lust of the mind, that "
280 "by a perseverance of delight in the continued and indefatigable generation "
281 "of knowledge, exceeds the short vehemence of any carnal pleasure.") != 0)
282         fail("hread result for base64");
283     if (hclose(fin) != 0) fail("hclose(\"data:;base64,...\")");
284 
285     kstring_t kstr = { 0, 0, NULL };
286 
287     if (strcmp(haddextension(&kstr, "foo/bar.bam", 0, ".bai"),
288                "foo/bar.bam.bai") != 0) fail("haddextension foo/bar.bam[.bai]");
289     if (strcmp(haddextension(&kstr, "foo/bar.bam", 1, ".bai"),
290                "foo/bar.bai") != 0) fail("haddextension foo/bar[.bai]");
291     if (strcmp(haddextension(&kstr, "foo.bar/baz", 1, ".bai"),
292                "foo.bar/baz.bai") != 0) fail("haddextension foo.bar/baz[.bai]");
293     if (strcmp(haddextension(&kstr, "foo#bar.bam", 0, ".bai"),
294                "foo#bar.bam.bai") != 0) fail("haddextension foo#bar.bam[.bai]");
295     if (strcmp(haddextension(&kstr, ".bam", 1, ".bai"),
296                ".bai") != 0) fail("haddextension [.bai]");
297     if (strcmp(haddextension(&kstr, "foo", 1, ".csi"),
298                "foo.csi") != 0) fail("haddextension foo[.csi]");
299 
300     if (strcmp(haddextension(&kstr, "http://host/bar.cram?a&b&c", 0, ".crai"),
301                "http://host/bar.cram.crai?a&b&c") != 0)
302         fail("haddextension http://host/bar.cram[.crai]?a&b&c");
303 
304     if (strcmp(haddextension(&kstr, "http://host/bar.cram#frag", 1, ".crai"),
305                "http://host/bar.crai#frag") != 0)
306         fail("haddextension http://host/bar[.crai]#frag");
307 
308     free(ks_release(&kstr));
309 
310     return EXIT_SUCCESS;
311 }
312