modified: src1/input.c
[GalaxyCodeBases.git] / c_cpp / lib / htslib / multipart.c
blob66d5707f02797040500b19330f97e0701f121e01
1 /* multipart.c -- GA4GH redirection and multipart backend for file streams.
3 Copyright (C) 2016 Genome Research Ltd.
5 Author: John Marshall <jm18@sanger.ac.uk>
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:
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
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. */
25 #include <config.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
31 #include "htslib/kstring.h"
33 #include "hts_internal.h"
34 #include "hfile_internal.h"
36 #ifndef EPROTO
37 #define EPROTO ENOEXEC
38 #endif
40 typedef struct hfile_part {
41 char *url;
42 char **headers;
43 } hfile_part;
45 typedef struct {
46 hFILE base;
47 hfile_part *parts;
48 size_t nparts, maxparts, current;
49 hFILE *currentfp;
50 } hFILE_multipart;
52 static void free_part(hfile_part *p)
54 free(p->url);
55 if (p->headers) {
56 char **hdr;
57 for (hdr = p->headers; *hdr; hdr++) free(*hdr);
58 free(p->headers);
61 p->url = NULL;
62 p->headers = NULL;
65 static void free_all_parts(hFILE_multipart *fp)
67 size_t i;
68 for (i = 0; i < fp->nparts; i++) free_part(&fp->parts[i]);
69 free(fp->parts);
72 static ssize_t multipart_read(hFILE *fpv, void *buffer, size_t nbytes)
74 hFILE_multipart *fp = (hFILE_multipart *) fpv;
75 size_t n;
77 open_next:
78 if (fp->currentfp == NULL) {
79 if (fp->current < fp->nparts) {
80 const hfile_part *p = &fp->parts[fp->current];
81 hts_log_debug("Opening part #%zu of %zu: \"%.120s%s\"",
82 fp->current+1, fp->nparts, p->url,
83 (strlen(p->url) > 120)? "..." : "");
85 fp->currentfp = p->headers?
86 hopen(p->url, "r:", "httphdr:v", p->headers, NULL)
87 : hopen(p->url, "r");
89 if (fp->currentfp == NULL) return -1;
91 else return 0; // No more parts, so we're truly at EOF
94 n = fp->currentfp->mobile?
95 fp->currentfp->backend->read(fp->currentfp, buffer, nbytes)
96 : hread(fp->currentfp, buffer, nbytes);
98 if (n == 0) {
99 // We're at EOF on this part, so set up the next part
100 hFILE *prevfp = fp->currentfp;
101 free_part(&fp->parts[fp->current]);
102 fp->current++;
103 fp->currentfp = NULL;
104 if (hclose(prevfp) < 0) return -1;
105 goto open_next;
108 return n; // Number of bytes read by (or an error from) fp->currentfp
111 static ssize_t multipart_write(hFILE *fpv, const void *buffer, size_t nbytes)
113 errno = EROFS;
114 return -1;
117 static off_t multipart_seek(hFILE *fpv, off_t offset, int whence)
119 errno = ESPIPE;
120 return -1;
123 static int multipart_close(hFILE *fpv)
125 hFILE_multipart *fp = (hFILE_multipart *) fpv;
127 free_all_parts(fp);
128 if (fp->currentfp) {
129 if (hclose(fp->currentfp) < 0) return -1;
132 return 0;
135 static const struct hFILE_backend multipart_backend =
137 multipart_read, multipart_write, multipart_seek, NULL, multipart_close
140 // Returns 'v' (valid value), 'i' (invalid; required GA4GH field missing),
141 // or upon encountering an unexpected token, that token's type.
142 // Explicit `return '?'` means a JSON parsing error, typically a member key
143 // that is not a string. An unexpected token may be a valid token that was
144 // not the type expected for a particular GA4GH field, or it may be '?' or
145 // '\0' which should be propagated.
146 static char
147 parse_ga4gh_redirect_json(hFILE_multipart *fp, hFILE *json,
148 kstring_t *b, kstring_t *header)
150 hts_json_token t;
152 if (hts_json_fnext(json, &t, b) != '{') return t.type;
153 while (hts_json_fnext(json, &t, b) != '}') {
154 if (t.type != 's') return '?';
156 if (strcmp(t.str, "urls") == 0) {
157 if (hts_json_fnext(json, &t, b) != '[') return t.type;
159 while (hts_json_fnext(json, &t, b) != ']') {
160 hfile_part *part;
161 size_t n = 0, max = 0;
163 hts_expand(hfile_part, fp->nparts+1, fp->maxparts, fp->parts);
164 part = &fp->parts[fp->nparts++];
165 part->url = NULL;
166 part->headers = NULL;
168 if (t.type != '{') return t.type;
169 while (hts_json_fnext(json, &t, b) != '}') {
170 if (t.type != 's') return '?';
172 if (strcmp(t.str, "url") == 0) {
173 if (hts_json_fnext(json, &t, b) != 's') return t.type;
174 part->url = ks_release(b);
176 else if (strcmp(t.str, "headers") == 0) {
177 if (hts_json_fnext(json, &t, b) != '{') return t.type;
179 while (hts_json_fnext(json, &t, header) != '}') {
180 if (t.type != 's') return '?';
182 if (hts_json_fnext(json, &t, b) != 's')
183 return t.type;
185 kputs(": ", header);
186 kputs(t.str, header);
187 n++;
188 hts_expand(char *, n+1, max, part->headers);
189 part->headers[n-1] = ks_release(header);
190 part->headers[n] = NULL;
193 else if (hts_json_fskip_value(json, '\0') != 'v')
194 return '?';
197 if (! part->url) return 'i';
200 else if (strcmp(t.str, "format") == 0) {
201 if (hts_json_fnext(json, &t, b) != 's') return t.type;
203 hts_log_debug("GA4GH JSON redirection to multipart %s data", t.str);
205 else if (hts_json_fskip_value(json, '\0') != 'v') return '?';
208 if (hts_json_fnext(json, &t, b) != '\0') return '?';
210 return 'v';
213 hFILE *hopen_json_redirect(hFILE *hfile, const char *mode)
215 hFILE_multipart *fp;
216 kstring_t s1 = { 0, 0, NULL }, s2 = { 0, 0, NULL };
217 char ret;
219 fp = (hFILE_multipart *) hfile_init(sizeof (hFILE_multipart), mode, 0);
220 if (fp == NULL) return NULL;
222 fp->parts = NULL;
223 fp->nparts = fp->maxparts = 0;
225 ret = parse_ga4gh_redirect_json(fp, hfile, &s1, &s2);
226 free(s1.s);
227 free(s2.s);
228 if (ret != 'v') {
229 free_all_parts(fp);
230 hfile_destroy((hFILE *) fp);
231 errno = (ret == '?' || ret == '\0')? EPROTO : EINVAL;
232 return NULL;
235 fp->current = 0;
236 fp->currentfp = NULL;
237 fp->base.backend = &multipart_backend;
238 return &fp->base;