Coverage Report

Created: 2026-10-17 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/htslib/hfile.c
Line
Count
Source
1
/*  hfile.c -- buffered low-level input/output streams.
2
3
    Copyright (C) 2013-2021, 2023-2025 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
#define HTS_BUILDING_LIBRARY // Enables HTSLIB_EXPORT, see htslib/hts_defs.h
26
#include <config.h>
27
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <stddef.h>
31
#include <string.h>
32
#include <errno.h>
33
#include <limits.h>
34
35
#include <pthread.h>
36
37
#ifdef ENABLE_PLUGINS
38
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__)
39
#define USING_WINDOWS_PLUGIN_DLLS
40
#include <dlfcn.h>
41
#endif
42
#endif
43
44
#include "htslib/hfile.h"
45
#include "hfile_internal.h"
46
#include "htslib/kstring.h"
47
48
#ifndef ENOTSUP
49
#define ENOTSUP EINVAL
50
#endif
51
#ifndef EOVERFLOW
52
#define EOVERFLOW ERANGE
53
#endif
54
#ifndef EPROTONOSUPPORT
55
#define EPROTONOSUPPORT ENOSYS
56
#endif
57
58
#ifndef SSIZE_MAX /* SSIZE_MAX is POSIX 1 */
59
#define SSIZE_MAX LONG_MAX
60
#endif
61
62
/* hFILE fields are used as follows:
63
64
   char *buffer;     // Pointer to the start of the I/O buffer
65
   char *begin;      // First not-yet-read character / unused position
66
   char *end;        // First unfilled/unfillable position
67
   char *limit;      // Pointer to the first position past the buffer
68
69
   const hFILE_backend *backend;  // Methods to refill/flush I/O buffer
70
71
   off_t offset;     // Offset within the stream of buffer position 0
72
   unsigned at_eof:1;// For reading, whether EOF has been seen
73
   unsigned mobile:1;// Buffer is a mobile window or fixed full contents
74
   unsigned readonly:1;// Whether opened as "r" rather than "r+"/"w"/"a"
75
   int has_errno;    // Error number from the last failure on this stream
76
77
For reading, begin is the first unread character in the buffer and end is the
78
first unfilled position:
79
80
   -----------ABCDEFGHIJKLMNO---------------
81
   ^buffer    ^begin         ^end           ^limit
82
83
For writing, begin is the first unused position and end is unused so remains
84
equal to buffer:
85
86
   ABCDEFGHIJKLMNOPQRSTUVWXYZ---------------
87
   ^buffer                   ^begin         ^limit
88
   ^end
89
90
Thus if begin > end then there is a non-empty write buffer, if begin < end
91
then there is a non-empty read buffer, and if begin == end then both buffers
92
are empty.  In all cases, the stream's file position indicator corresponds
93
to the position pointed to by begin.
94
95
The above is the normal scenario of a mobile window.  For in-memory
96
streams (eg via hfile_init_fixed) the buffer can be used as the full
97
contents without any separate backend behind it.  These always have at_eof
98
set, offset set to 0, need no read() method, and should just return EINVAL
99
for seek():
100
101
   abcdefghijkLMNOPQRSTUVWXYZ------
102
   ^buffer    ^begin         ^end  ^limit
103
*/
104
HTSLIB_EXPORT
105
hFILE *hfile_init(size_t struct_size, const char *mode, size_t capacity)
106
31.4k
{
107
31.4k
    hFILE *fp = (hFILE *) malloc(struct_size);
108
31.4k
    if (fp == NULL) goto error;
109
110
31.4k
    const int maxcap = 128*1024;
111
112
31.4k
    if (capacity == 0) capacity = maxcap;
113
    // FIXME For now, clamp input buffer sizes so mpileup doesn't eat memory
114
31.4k
    if (strchr(mode, 'r') && capacity > maxcap) capacity = maxcap;
115
116
31.4k
#ifdef HAVE_POSIX_MEMALIGN
117
31.4k
    fp->buffer = NULL;
118
31.4k
    if (posix_memalign((void **)&fp->buffer, 256, capacity) < 0)
119
0
        goto error;
120
#else
121
    fp->buffer = (char *) malloc(capacity);
122
    if (fp->buffer == NULL) goto error;
123
#endif
124
125
31.4k
    fp->begin = fp->end = fp->buffer;
126
31.4k
    fp->limit = &fp->buffer[capacity];
127
128
31.4k
    fp->offset = 0;
129
31.4k
    fp->at_eof = 0;
130
31.4k
    fp->mobile = 1;
131
31.4k
    fp->readonly = (strchr(mode, 'r') && ! strchr(mode, '+'));
132
31.4k
    fp->preserve = 0;
133
31.4k
    fp->has_errno = 0;
134
31.4k
    return fp;
135
136
0
error:
137
0
    hfile_destroy(fp);
138
0
    return NULL;
139
31.4k
}
140
141
hFILE *hfile_init_fixed(size_t struct_size, const char *mode,
142
                        char *buffer, size_t buf_filled, size_t buf_size)
143
44.7k
{
144
44.7k
    hFILE *fp = (hFILE *) malloc(struct_size);
145
44.7k
    if (fp == NULL) return NULL;
146
147
44.7k
    fp->buffer = fp->begin = buffer;
148
44.7k
    fp->end = &fp->buffer[buf_filled];
149
44.7k
    fp->limit = &fp->buffer[buf_size];
150
151
44.7k
    fp->offset = 0;
152
44.7k
    fp->at_eof = 1;
153
44.7k
    fp->mobile = 0;
154
44.7k
    fp->readonly = (strchr(mode, 'r') && ! strchr(mode, '+'));
155
44.7k
    fp->preserve = 0;
156
44.7k
    fp->has_errno = 0;
157
44.7k
    return fp;
158
44.7k
}
159
160
static const struct hFILE_backend mem_backend;
161
162
HTSLIB_EXPORT
163
void hfile_destroy(hFILE *fp)
164
76.3k
{
165
76.3k
    int save = errno;
166
76.3k
    if (fp) free(fp->buffer);
167
76.3k
    free(fp);
168
76.3k
    errno = save;
169
76.3k
}
170
171
static inline int writebuffer_is_nonempty(hFILE *fp)
172
2.93M
{
173
2.93M
    return fp->begin > fp->end;
174
2.93M
}
175
176
/* Refills the read buffer from the backend (once, so may only partially
177
   fill the buffer), returning the number of additional characters read
178
   (which might be 0), or negative when an error occurred.  */
179
static ssize_t refill_buffer(hFILE *fp)
180
78.8k
{
181
78.8k
    ssize_t n;
182
183
    // Move any unread characters to the start of the buffer
184
78.8k
    if (fp->mobile && fp->begin > fp->buffer) {
185
0
        fp->offset += fp->begin - fp->buffer;
186
0
        memmove(fp->buffer, fp->begin, fp->end - fp->begin);
187
0
        fp->end = &fp->buffer[fp->end - fp->begin];
188
0
        fp->begin = fp->buffer;
189
0
    }
190
191
    // Read into the available buffer space at fp->[end,limit)
192
78.8k
    if (fp->at_eof || fp->end == fp->limit) n = 0;
193
24
    else {
194
24
        n = fp->backend->read(fp, fp->end, fp->limit - fp->end);
195
24
        if (n < 0) { fp->has_errno = errno; return n; }
196
2
        else if (n == 0) fp->at_eof = 1;
197
24
    }
198
199
78.8k
    fp->end += n;
200
78.8k
    return n;
201
78.8k
}
202
203
/*
204
 * Changes the buffer size for an hFILE.  Ideally this is done
205
 * immediately after opening.  If performed later, this function may
206
 * fail if we are reducing the buffer size and the current offset into
207
 * the buffer is beyond the new capacity.
208
 *
209
 * Returns 0 on success;
210
 *        -1 on failure.
211
 */
212
HTSLIB_EXPORT
213
0
int hfile_set_blksize(hFILE *fp, size_t bufsiz) {
214
0
    char *buffer;
215
0
    ptrdiff_t curr_used;
216
0
    if (!fp) return -1;
217
0
    curr_used = (fp->begin > fp->end ? fp->begin : fp->end) - fp->buffer;
218
0
    if (bufsiz == 0) bufsiz = 32768;
219
220
    // Ensure buffer resize will not erase live data
221
0
    if (bufsiz < curr_used)
222
0
        return -1;
223
224
0
    if (!(buffer = (char *) realloc(fp->buffer, bufsiz))) return -1;
225
226
0
    fp->begin  = buffer + (fp->begin - fp->buffer);
227
0
    fp->end    = buffer + (fp->end   - fp->buffer);
228
0
    fp->buffer = buffer;
229
0
    fp->limit  = &fp->buffer[bufsiz];
230
231
0
    return 0;
232
0
}
233
234
/* Called only from hgetc(), when our buffer is empty.  */
235
HTSLIB_EXPORT
236
int hgetc2(hFILE *fp)
237
422
{
238
422
    return (refill_buffer(fp) > 0)? (unsigned char) *(fp->begin++) : EOF;
239
422
}
240
241
ssize_t hgetdelim(char *buffer, size_t size, int delim, hFILE *fp)
242
2.84M
{
243
2.84M
    char *found;
244
2.84M
    size_t n, copied = 0;
245
2.84M
    ssize_t got;
246
247
2.84M
    if (size < 1 || size > SSIZE_MAX) {
248
0
        fp->has_errno = errno = EINVAL;
249
0
        return -1;
250
0
    }
251
2.84M
    if (writebuffer_is_nonempty(fp)) {
252
0
        fp->has_errno = errno = EBADF;
253
0
        return -1;
254
0
    }
255
256
2.84M
    --size; /* to allow space for the NUL terminator */
257
258
2.84M
    do {
259
2.84M
        n = fp->end - fp->begin;
260
2.84M
        if (n > size - copied) n = size - copied;
261
262
        /* Look in the hFILE buffer for the delimiter */
263
2.84M
        found = memchr(fp->begin, delim, n);
264
2.84M
        if (found != NULL) {
265
2.78M
            n = found - fp->begin + 1;
266
2.78M
            memcpy(buffer + copied, fp->begin, n);
267
2.78M
            buffer[n + copied] = '\0';
268
2.78M
            fp->begin += n;
269
2.78M
            return n + copied;
270
2.78M
        }
271
272
        /* No delimiter yet, copy as much as we can and refill if necessary */
273
65.0k
        memcpy(buffer + copied, fp->begin, n);
274
65.0k
        fp->begin += n;
275
65.0k
        copied += n;
276
277
65.0k
        if (copied == size) { /* Output buffer full */
278
25.4k
            buffer[copied] = '\0';
279
25.4k
            return copied;
280
25.4k
        }
281
282
39.6k
        got = refill_buffer(fp);
283
39.6k
    } while (got > 0);
284
285
39.6k
    if (got < 0) return -1; /* Error on refill. */
286
287
39.6k
    buffer[copied] = '\0';  /* EOF, return anything that was copied. */
288
39.6k
    return copied;
289
39.6k
}
290
291
char *hgets(char *buffer, int size, hFILE *fp)
292
0
{
293
0
    if (size < 1) {
294
0
        fp->has_errno = errno = EINVAL;
295
0
        return NULL;
296
0
    }
297
0
    return hgetln(buffer, size, fp) > 0 ? buffer : NULL;
298
0
}
299
300
// Wrap around hgets() to get the right signature for kgets_func
301
static char *hgets_wrapper(char *buffer, int size, void *fp)
302
0
{
303
0
    return hgets(buffer, size, (hFILE *) fp);
304
0
}
305
306
int khgetline(struct kstring_t *kstr, hFILE *fp)
307
0
{
308
0
    if (!kstr || !fp)
309
0
        return EOF;
310
0
    return kgetline(kstr, hgets_wrapper, fp);
311
0
}
312
313
ssize_t hpeek(hFILE *fp, void *buffer, size_t nbytes)
314
127k
{
315
127k
    size_t n = fp->end - fp->begin;
316
127k
    while (n < nbytes) {
317
38.7k
        ssize_t ret = refill_buffer(fp);
318
38.7k
        if (ret < 0) return ret;
319
38.7k
        else if (ret == 0) break;
320
1
        else n += ret;
321
38.7k
    }
322
323
127k
    if (n > nbytes) n = nbytes;
324
127k
    memcpy(buffer, fp->begin, n);
325
127k
    return n;
326
127k
}
327
328
/* Called only from hread(); when called, our buffer is empty and nread bytes
329
   have already been placed in the destination buffer.  */
330
HTSLIB_EXPORT
331
ssize_t hread2(hFILE *fp, void *destv, size_t nbytes, size_t nread)
332
4
{
333
4
    const size_t capacity = fp->limit - fp->buffer;
334
4
    int buffer_invalidated = 0;
335
4
    char *dest = (char *) destv;
336
4
    dest += nread, nbytes -= nread;
337
338
    // Read large requests directly into the destination buffer
339
6
    while (nbytes * 2 >= capacity && !fp->at_eof) {
340
3
        ssize_t n = fp->backend->read(fp, dest, nbytes);
341
3
        if (n < 0) { fp->has_errno = errno; return n; }
342
2
        else if (n == 0) fp->at_eof = 1;
343
1
        else buffer_invalidated = 1;
344
2
        fp->offset += n;
345
2
        dest += n, nbytes -= n;
346
2
        nread += n;
347
2
    }
348
349
3
    if (buffer_invalidated) {
350
        // Our unread buffer is empty, so begin == end, but our already-read
351
        // buffer [buffer,begin) is likely non-empty and is no longer valid as
352
        // its contents are no longer adjacent to the file position indicator.
353
        // Discard it so that hseek() can't try to take advantage of it.
354
1
        fp->offset += fp->begin - fp->buffer;
355
1
        fp->begin = fp->end = fp->buffer;
356
1
    }
357
358
3
    while (nbytes > 0 && !fp->at_eof) {
359
0
        size_t n;
360
0
        ssize_t ret = refill_buffer(fp);
361
0
        if (ret < 0) return ret;
362
363
0
        n = fp->end - fp->begin;
364
0
        if (n > nbytes) n = nbytes;
365
0
        memcpy(dest, fp->begin, n);
366
0
        fp->begin += n;
367
0
        dest += n, nbytes -= n;
368
0
        nread += n;
369
0
    }
370
371
3
    return nread;
372
3
}
373
374
/* Flushes the write buffer, fp->[buffer,begin), out through the backend
375
   returning 0 on success or negative if an error occurred.  */
376
static ssize_t flush_buffer(hFILE *fp)
377
89.3k
{
378
89.3k
    const char *buffer = fp->buffer;
379
172k
    while (buffer < fp->begin) {
380
83.6k
        ssize_t n = fp->backend->write(fp, buffer, fp->begin - buffer);
381
83.6k
        if (n < 0) { fp->has_errno = errno; return n; }
382
83.6k
        buffer += n;
383
83.6k
        fp->offset += n;
384
83.6k
    }
385
386
89.3k
    fp->begin = fp->buffer;  // Leave the buffer empty
387
89.3k
    return 0;
388
89.3k
}
389
390
int hflush(hFILE *fp)
391
40.3k
{
392
40.3k
    if (flush_buffer(fp) < 0) return EOF;
393
40.3k
    if (fp->backend->flush) {
394
40.3k
        if (fp->backend->flush(fp) < 0) { fp->has_errno = errno; return EOF; }
395
40.3k
    }
396
40.3k
    return 0;
397
40.3k
}
398
399
/* Called only from hputc(), when our buffer is already full.  */
400
HTSLIB_EXPORT
401
int hputc2(int c, hFILE *fp)
402
184
{
403
184
    if (flush_buffer(fp) < 0) return EOF;
404
184
    *(fp->begin++) = c;
405
184
    return c;
406
184
}
407
408
/* Called only from hwrite() and hputs2(); when called, our buffer is either
409
   full and ncopied bytes from the source have already been copied to our
410
   buffer; or completely empty, ncopied is zero and totalbytes is greater than
411
   the buffer size.  */
412
HTSLIB_EXPORT
413
ssize_t hwrite2(hFILE *fp, const void *srcv, size_t totalbytes, size_t ncopied)
414
48.8k
{
415
48.8k
    const char *src = (const char *) srcv;
416
48.8k
    ssize_t ret;
417
48.8k
    const size_t capacity = fp->limit - fp->buffer;
418
48.8k
    size_t remaining = totalbytes - ncopied;
419
48.8k
    src += ncopied;
420
421
48.8k
    ret = flush_buffer(fp);
422
48.8k
    if (ret < 0) return ret;
423
424
    // Write large blocks out directly from the source buffer
425
52.5k
    while (remaining * 2 >= capacity) {
426
3.70k
        ssize_t n = fp->backend->write(fp, src, remaining);
427
3.70k
        if (n < 0) { fp->has_errno = errno; return n; }
428
3.70k
        fp->offset += n;
429
3.70k
        src += n, remaining -= n;
430
3.70k
    }
431
432
    // Just buffer any remaining characters
433
48.8k
    memcpy(fp->begin, src, remaining);
434
48.8k
    fp->begin += remaining;
435
436
48.8k
    return totalbytes;
437
48.8k
}
438
439
/* Called only from hputs(), when our buffer is already full.  */
440
HTSLIB_EXPORT
441
int hputs2(const char *text, size_t totalbytes, size_t ncopied, hFILE *fp)
442
0
{
443
0
    return (hwrite2(fp, text, totalbytes, ncopied) >= 0)? 0 : EOF;
444
0
}
445
446
off_t hseek(hFILE *fp, off_t offset, int whence)
447
5.34k
{
448
5.34k
    off_t curpos, pos;
449
450
5.34k
    if (writebuffer_is_nonempty(fp) && fp->mobile) {
451
0
        int ret = flush_buffer(fp);
452
0
        if (ret < 0) return ret;
453
0
    }
454
455
5.34k
    curpos = htell(fp);
456
457
    // Relative offsets are given relative to the hFILE's stream position,
458
    // which may differ from the backend's physical position due to buffering
459
    // read-ahead.  Correct for this by converting to an absolute position.
460
5.34k
    if (whence == SEEK_CUR) {
461
0
        if (curpos + offset < 0) {
462
            // Either a negative offset resulted in a position before the
463
            // start of the file, or we overflowed when given a positive offset
464
0
            fp->has_errno = errno = (offset < 0)? EINVAL : EOVERFLOW;
465
0
            return -1;
466
0
        }
467
468
0
        whence = SEEK_SET;
469
0
        offset = curpos + offset;
470
0
    }
471
    // For fixed immobile buffers, convert everything else to SEEK_SET too
472
    // so that seeking can be avoided for all (within range) requests.
473
5.34k
    else if (! fp->mobile && whence == SEEK_END) {
474
2.90k
        size_t length = fp->end - fp->buffer;
475
2.90k
        if (offset > 0 || -offset > length) {
476
408
            fp->has_errno = errno = EINVAL;
477
408
            return -1;
478
408
        }
479
480
2.49k
        whence = SEEK_SET;
481
2.49k
        offset = length + offset;
482
2.49k
    }
483
484
    // Avoid seeking if the desired position is within our read buffer.
485
    // (But not when the next operation may be a write on a mobile buffer.)
486
4.93k
    if (whence == SEEK_SET && (! fp->mobile || fp->readonly) &&
487
4.93k
        offset >= fp->offset && offset - fp->offset <= fp->end - fp->buffer) {
488
4.93k
        fp->begin = &fp->buffer[offset - fp->offset];
489
4.93k
        return offset;
490
4.93k
    }
491
492
0
    pos = fp->backend->seek(fp, offset, whence);
493
0
    if (pos < 0) { fp->has_errno = errno; return pos; }
494
495
    // Seeking succeeded, so discard any non-empty read buffer
496
0
    fp->begin = fp->end = fp->buffer;
497
0
    fp->at_eof = 0;
498
499
0
    fp->offset = pos;
500
0
    return pos;
501
0
}
502
503
int hclose(hFILE *fp)
504
75.7k
{
505
75.7k
    int err = fp->has_errno;
506
507
75.7k
    if (writebuffer_is_nonempty(fp) && hflush(fp) < 0) err = fp->has_errno;
508
75.7k
    if (!fp->preserve) {
509
75.7k
        if (fp->backend->close(fp) < 0) err = errno;
510
75.7k
        hfile_destroy(fp);
511
75.7k
    }
512
513
75.7k
    if (err) {
514
0
        errno = err;
515
0
        return EOF;
516
0
    }
517
75.7k
    else return 0;
518
75.7k
}
519
520
void hclose_abruptly(hFILE *fp)
521
23
{
522
23
    int save = errno;
523
23
    if (fp->preserve)
524
0
        return;
525
23
    if (fp->backend->close(fp) < 0) { /* Ignore subsequent errors */ }
526
23
    hfile_destroy(fp);
527
23
    errno = save;
528
23
}
529
530
531
/***************************
532
 * File descriptor backend *
533
 ***************************/
534
535
#ifndef _WIN32
536
#include <sys/socket.h>
537
#include <sys/stat.h>
538
#define HAVE_STRUCT_STAT_ST_BLKSIZE
539
#else
540
#include <winsock2.h>
541
#define HAVE_CLOSESOCKET
542
#define HAVE_SETMODE
543
#endif
544
#include <fcntl.h>
545
#include <unistd.h>
546
547
/* For Unix, it doesn't matter whether a file descriptor is a socket.
548
   However Windows insists on send()/recv() and its own closesocket()
549
   being used when fd happens to be a socket.  */
550
551
typedef struct {
552
    hFILE base;
553
    int fd;
554
    unsigned is_socket:1, is_shared:1;
555
} hFILE_fd;
556
557
static ssize_t fd_read(hFILE *fpv, void *buffer, size_t nbytes)
558
27
{
559
27
    hFILE_fd *fp = (hFILE_fd *) fpv;
560
27
    ssize_t n;
561
27
    do {
562
27
        n = fp->is_socket? recv(fp->fd, buffer, nbytes, 0)
563
27
                         : read(fp->fd, buffer, nbytes);
564
27
    } while (n < 0 && errno == EINTR);
565
27
    return n;
566
27
}
567
568
static ssize_t fd_write(hFILE *fpv, const void *buffer, size_t nbytes)
569
87.3k
{
570
87.3k
    hFILE_fd *fp = (hFILE_fd *) fpv;
571
87.3k
    ssize_t n;
572
87.3k
    do {
573
87.3k
        n = fp->is_socket?  send(fp->fd, buffer, nbytes, 0)
574
87.3k
                         : write(fp->fd, buffer, nbytes);
575
87.3k
    } while (n < 0 && errno == EINTR);
576
#ifdef _WIN32
577
        // On windows we have no SIGPIPE.  Instead write returns
578
        // EINVAL.  We check for this and our fd being a pipe.
579
        // If so, we raise SIGTERM instead of SIGPIPE.  It's not
580
        // ideal, but I think the only alternative is extra checking
581
        // in every single piece of code.
582
        if (n < 0 && errno == EINVAL &&
583
            GetLastError() == ERROR_NO_DATA &&
584
            GetFileType((HANDLE)_get_osfhandle(fp->fd)) == FILE_TYPE_PIPE) {
585
            raise(SIGTERM);
586
        }
587
#endif
588
87.3k
    return n;
589
87.3k
}
590
591
static off_t fd_seek(hFILE *fpv, off_t offset, int whence)
592
0
{
593
0
    hFILE_fd *fp = (hFILE_fd *) fpv;
594
#ifdef _WIN32
595
    // On windows lseek can return non-zero values even on a pipe.  Instead
596
    // it's likely to seek somewhere within the pipe memory buffer.
597
    // This breaks bgzf_check_EOF among other things.
598
    if (GetFileType((HANDLE)_get_osfhandle(fp->fd)) == FILE_TYPE_PIPE) {
599
        errno = ESPIPE;
600
        return -1;
601
    }
602
#endif
603
604
0
    return lseek(fp->fd, offset, whence);
605
0
}
606
607
static int fd_flush(hFILE *fpv)
608
40.3k
{
609
40.3k
    int ret = 0;
610
40.3k
    do {
611
40.3k
#ifdef HAVE_FDATASYNC
612
40.3k
        hFILE_fd *fp = (hFILE_fd *) fpv;
613
40.3k
        ret = fdatasync(fp->fd);
614
#elif defined(HAVE_FSYNC)
615
        hFILE_fd *fp = (hFILE_fd *) fpv;
616
        ret = fsync(fp->fd);
617
#endif
618
        // Ignore invalid-for-fsync(2) errors due to being, e.g., a pipe,
619
        // and operation-not-supported errors (Mac OS X)
620
40.3k
        if (ret < 0 && (errno == EINVAL || errno == ENOTSUP)) ret = 0;
621
40.3k
    } while (ret < 0 && errno == EINTR);
622
40.3k
    return ret;
623
40.3k
}
624
625
static int fd_close(hFILE *fpv)
626
31.0k
{
627
31.0k
    hFILE_fd *fp = (hFILE_fd *) fpv;
628
31.0k
    int ret;
629
630
    // If we don't own the fd, return successfully without actually closing it
631
31.0k
    if (fp->is_shared) return 0;
632
633
31.0k
    do {
634
#ifdef HAVE_CLOSESOCKET
635
        ret = fp->is_socket? closesocket(fp->fd) : close(fp->fd);
636
#else
637
31.0k
        ret = close(fp->fd);
638
31.0k
#endif
639
31.0k
    } while (ret < 0 && errno == EINTR);
640
31.0k
    return ret;
641
31.0k
}
642
643
static const struct hFILE_backend fd_backend =
644
{
645
    fd_read, fd_write, fd_seek, fd_flush, fd_close
646
};
647
648
static size_t blksize(int fd)
649
31.0k
{
650
31.0k
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
651
31.0k
    struct stat sbuf;
652
31.0k
    if (fstat(fd, &sbuf) != 0) return 0;
653
654
    // Pipes/FIFOs on linux return 4Kb here often, but it's much too small
655
    // for performant I/O.
656
31.0k
    return S_ISFIFO(sbuf.st_mode)
657
31.0k
        ? 128*1024
658
31.0k
        : sbuf.st_blksize;
659
#else
660
    return 0;
661
#endif
662
31.0k
}
663
664
static hFILE *hopen_fd(const char *filename, const char *mode)
665
31.2k
{
666
31.2k
    hFILE_fd *fp = NULL;
667
31.2k
    int fd = open(filename, hfile_oflags(mode), 0666);
668
31.2k
    if (fd < 0) goto error;
669
670
31.0k
    fp = (hFILE_fd *) hfile_init(sizeof (hFILE_fd), mode, blksize(fd));
671
31.0k
    if (fp == NULL) goto error;
672
673
31.0k
    fp->fd = fd;
674
31.0k
    fp->is_socket = 0;
675
31.0k
    fp->is_shared = 0;
676
31.0k
    fp->base.backend = &fd_backend;
677
31.0k
    return &fp->base;
678
679
205
error:
680
205
    if (fd >= 0) { int save = errno; (void) close(fd); errno = save; }
681
205
    hfile_destroy((hFILE *) fp);
682
205
    return NULL;
683
31.0k
}
684
685
// Loads the contents of filename to produced a read-only, in memory,
686
// immobile hfile.  fp is the already opened file.  We always close this
687
// input fp, irrespective of whether we error or whether we return a new
688
// immobile hfile.
689
46
static hFILE *hpreload(hFILE *fp) {
690
46
    hFILE *mem_fp;
691
46
    char *buf = NULL;
692
46
    off_t buf_sz = 0, buf_a = 0, buf_inc = 8192, len;
693
694
156
    for (;;) {
695
156
        if (buf_a - buf_sz < 5000) {
696
133
            buf_a += buf_inc;
697
133
            char *t = realloc(buf, buf_a);
698
133
            if (!t) goto err;
699
133
            buf = t;
700
133
            if (buf_inc < 1000000) buf_inc *= 1.3;
701
133
        }
702
156
        len = hread(fp, buf+buf_sz, buf_a-buf_sz);
703
156
        if (len > 0)
704
110
            buf_sz += len;
705
46
        else
706
46
            break;
707
156
    }
708
709
46
    if (len < 0) goto err;
710
45
    mem_fp = hfile_init_fixed(sizeof(hFILE), "r", buf, buf_sz, buf_a);
711
45
    if (!mem_fp) goto err;
712
45
    mem_fp->backend = &mem_backend;
713
714
45
    if (hclose(fp) < 0) {
715
0
        hclose_abruptly(mem_fp);
716
0
        goto err;
717
0
    }
718
45
    return mem_fp;
719
720
1
 err:
721
1
    free(buf);
722
1
    hclose_abruptly(fp);
723
1
    return NULL;
724
45
}
725
726
87
static int is_preload_url_remote(const char *url){
727
87
    return hisremote(url + 8); // len("preload:") = 8
728
87
}
729
730
93
static hFILE *hopen_preload(const char *url, const char *mode){
731
93
    hFILE* fp = hopen(url + 8, mode);
732
93
    return fp ? hpreload(fp) : NULL;
733
93
}
734
735
hFILE *hdopen(int fd, const char *mode)
736
2
{
737
2
    hFILE_fd *fp = (hFILE_fd*) hfile_init(sizeof (hFILE_fd), mode, blksize(fd));
738
2
    if (fp == NULL) return NULL;
739
740
2
    fp->fd = fd;
741
2
    fp->is_socket = (strchr(mode, 's') != NULL);
742
2
    fp->is_shared = (strchr(mode, 'S') != NULL);
743
2
    fp->base.backend = &fd_backend;
744
2
    return &fp->base;
745
2
}
746
747
static hFILE *hopen_fd_fileuri(const char *url, const char *mode)
748
21
{
749
21
    if (strncmp(url, "file://localhost/", 17) == 0) url += 16;
750
21
    else if (strncmp(url, "file:///", 8) == 0) url += 7;
751
21
    else { errno = EPROTONOSUPPORT; return NULL; }
752
753
#if defined(_WIN32) || defined(__MSYS__)
754
    // For cases like C:/foo
755
    if (url[0] == '/' && url[1] && url[2] == ':' && url[3] == '/') url++;
756
#endif
757
758
0
    return hopen_fd(url, mode);
759
21
}
760
761
static hFILE *hopen_fd_stdinout(const char *mode)
762
2
{
763
2
    int fd = (strchr(mode, 'r') != NULL)? STDIN_FILENO : STDOUT_FILENO;
764
2
    char mode_shared[101];
765
2
    snprintf(mode_shared, sizeof mode_shared, "S%s", mode);
766
#if defined HAVE_SETMODE && defined O_BINARY
767
    if (setmode(fd, O_BINARY) < 0) return NULL;
768
#endif
769
2
    return hdopen(fd, mode_shared);
770
2
}
771
772
HTSLIB_EXPORT
773
int hfile_oflags(const char *mode)
774
31.2k
{
775
31.2k
    int rdwr = 0, flags = 0;
776
31.2k
    const char *s;
777
82.2k
    for (s = mode; *s; s++)
778
51.0k
        switch (*s) {
779
229
        case 'r': rdwr = O_RDONLY;  break;
780
30.9k
        case 'w': rdwr = O_WRONLY; flags |= O_CREAT | O_TRUNC;  break;
781
0
        case 'a': rdwr = O_WRONLY; flags |= O_CREAT | O_APPEND;  break;
782
0
        case '+': rdwr = O_RDWR;  break;
783
0
#ifdef O_CLOEXEC
784
0
        case 'e': flags |= O_CLOEXEC;  break;
785
0
#endif
786
0
#ifdef O_EXCL
787
0
        case 'x': flags |= O_EXCL;  break;
788
0
#endif
789
19.7k
        default:  break;
790
51.0k
        }
791
792
#ifdef O_BINARY
793
    flags |= O_BINARY;
794
#endif
795
796
31.2k
    return rdwr | flags;
797
31.2k
}
798
799
800
/*********************
801
 * In-memory backend *
802
 *********************/
803
804
#include "hts_internal.h"
805
806
typedef struct {
807
    hFILE base;
808
} hFILE_mem;
809
810
static off_t mem_seek(hFILE *fpv, off_t offset, int whence)
811
0
{
812
0
    errno = EINVAL;
813
0
    return -1;
814
0
}
815
816
static int mem_close(hFILE *fpv)
817
44.7k
{
818
44.7k
    return 0;
819
44.7k
}
820
821
static const struct hFILE_backend mem_backend =
822
{
823
    NULL, NULL, mem_seek, NULL, mem_close
824
};
825
826
static int cmp_prefix(const char *key, const char *s)
827
351
{
828
354
    while (*key)
829
354
        if (tolower_c(*s) != *key) return +1;
830
3
        else s++, key++;
831
832
0
    return 0;
833
351
}
834
835
static hFILE *create_hfile_mem(char* buffer, const char* mode, size_t buf_filled, size_t buf_size)
836
44.6k
{
837
44.6k
    hFILE_mem *fp = (hFILE_mem *) hfile_init_fixed(sizeof(hFILE_mem), mode, buffer, buf_filled, buf_size);
838
44.6k
    if (fp == NULL)
839
0
        return NULL;
840
841
44.6k
    fp->base.backend = &mem_backend;
842
44.6k
    return &fp->base;
843
44.6k
}
844
845
static hFILE *hopen_mem(const char *url, const char *mode)
846
742
{
847
742
    size_t length, size;
848
742
    char *buffer;
849
742
    const char *data, *comma = strchr(url, ',');
850
742
    if (comma == NULL) { errno = EINVAL; return NULL; }
851
685
    data = comma+1;
852
853
    // TODO Implement write modes
854
685
    if (strchr(mode, 'r') == NULL) { errno = EROFS; return NULL; }
855
856
573
    if (comma - url >= 7 && cmp_prefix(";base64", &comma[-7]) == 0) {
857
0
        size = hts_base64_decoded_length(strlen(data));
858
0
        buffer = malloc(size);
859
0
        if (buffer == NULL) return NULL;
860
0
        hts_decode_base64(buffer, &length, data);
861
0
    }
862
573
    else {
863
573
        size = strlen(data) + 1;
864
573
        buffer = malloc(size);
865
573
        if (buffer == NULL) return NULL;
866
573
        hts_decode_percent(buffer, &length, data);
867
573
    }
868
573
    hFILE* hf;
869
870
573
    if(!(hf = create_hfile_mem(buffer, mode, length, size))){
871
0
        free(buffer);
872
0
        return NULL;
873
0
    }
874
875
573
    return hf;
876
573
}
877
878
static hFILE *hopenv_mem(const char *filename, const char *mode, va_list args)
879
44.1k
{
880
44.1k
    char* buffer = va_arg(args, char*);
881
44.1k
    size_t sz = va_arg(args, size_t);
882
44.1k
    va_end(args);
883
884
44.1k
    hFILE* hf;
885
886
44.1k
    if(!(hf = create_hfile_mem(buffer, mode, sz, sz))){
887
0
        free(buffer);
888
0
        return NULL;
889
0
    }
890
891
44.1k
    return hf;
892
44.1k
}
893
894
0
char *hfile_mem_get_buffer(hFILE *file, size_t *length) {
895
0
    if (file->backend != &mem_backend) {
896
0
        errno = EINVAL;
897
0
        return NULL;
898
0
    }
899
900
0
    if (length)
901
0
        *length = file->buffer - file->limit;
902
903
0
    return file->buffer;
904
0
}
905
906
0
char *hfile_mem_steal_buffer(hFILE *file, size_t *length) {
907
0
    char *buf = hfile_mem_get_buffer(file, length);
908
0
    if (buf)
909
0
        file->buffer = NULL;
910
0
    return buf;
911
0
}
912
913
// open() stub for mem: which only works with the vopen() interface
914
// Use 'data:,' for data encoded in the URL
915
0
static hFILE *hopen_not_supported(const char *fname, const char *mode) {
916
0
    errno = EINVAL;
917
0
    return NULL;
918
0
}
919
920
int hfile_plugin_init_mem(struct hFILE_plugin *self)
921
1
{
922
    // mem files are declared remote so they work with a tabix index
923
1
    static const struct hFILE_scheme_handler handler =
924
1
            {hopen_not_supported, hfile_always_remote, "mem", 2000 + 50, hopenv_mem};
925
1
    self->name = "mem";
926
1
    hfile_add_scheme_handler("mem", &handler);
927
1
    return 0;
928
1
}
929
930
/**********************************************************************
931
 * Dummy crypt4gh plug-in.  Does nothing apart from advise how to get *
932
 * the real one.  It will be overridden by the actual plug-in.        *
933
 **********************************************************************/
934
935
static hFILE *crypt4gh_needed(const char *url, const char *mode)
936
1
{
937
1
    const char *u = strncmp(url, "crypt4gh:", 9) == 0 ? url + 9 : url;
938
#if defined(ENABLE_PLUGINS)
939
    const char *enable_plugins = "";
940
#else
941
1
    const char *enable_plugins = "You also need to rebuild HTSlib with plug-ins enabled.\n";
942
1
#endif
943
944
1
    hts_log_error("Accessing \"%s\" needs the crypt4gh plug-in.\n"
945
1
                  "It can be found at "
946
1
                  "https://github.com/samtools/htslib-crypt4gh\n"
947
1
                  "%s"
948
1
                  "If you have the plug-in, please ensure it can be "
949
1
                  "found on your HTS_PATH.",
950
1
                  u, enable_plugins);
951
952
1
    errno = EPROTONOSUPPORT;
953
1
    return NULL;
954
1
}
955
956
int hfile_plugin_init_crypt4gh_needed(struct hFILE_plugin *self)
957
1
{
958
1
    static const struct hFILE_scheme_handler handler =
959
1
        { crypt4gh_needed, hfile_always_local, "crypt4gh-needed", 0, NULL };
960
1
    self->name = "crypt4gh-needed";
961
1
    hfile_add_scheme_handler("crypt4gh", &handler);
962
1
    return 0;
963
1
}
964
965
966
/*****************************************
967
 * Plugin and hopen() backend dispatcher *
968
 *****************************************/
969
970
#include "htslib/khash.h"
971
972
0
KHASH_MAP_INIT_STR(scheme_string, const struct hFILE_scheme_handler *)
973
0
static khash_t(scheme_string) *schemes = NULL;
974
0
975
0
struct hFILE_plugin_list {
976
0
    struct hFILE_plugin plugin;
977
0
    struct hFILE_plugin_list *next;
978
0
};
979
0
980
0
static struct hFILE_plugin_list *plugins = NULL;
981
0
static pthread_mutex_t plugins_lock = PTHREAD_MUTEX_INITIALIZER;
982
0
983
0
void hfile_shutdown(int do_close_plugin)
984
1
{
985
1
    pthread_mutex_lock(&plugins_lock);
986
987
1
    if (schemes) {
988
1
        kh_destroy(scheme_string, schemes);
989
1
        schemes = NULL;
990
1
    }
991
992
6
    while (plugins != NULL) {
993
5
        struct hFILE_plugin_list *p = plugins;
994
5
        if (p->plugin.destroy) p->plugin.destroy();
995
#ifdef ENABLE_PLUGINS
996
        if (p->plugin.obj && do_close_plugin) close_plugin(p->plugin.obj);
997
#endif
998
5
        plugins = p->next;
999
5
        free(p);
1000
5
    }
1001
1002
1
    pthread_mutex_unlock(&plugins_lock);
1003
1
}
1004
1005
static void hfile_exit(void)
1006
1
{
1007
1
    hfile_shutdown(0);
1008
1
    pthread_mutex_destroy(&plugins_lock);
1009
1
}
1010
1011
static inline int priority(const struct hFILE_scheme_handler *handler)
1012
2
{
1013
2
    return handler->priority % 1000;
1014
2
}
1015
1016
#ifdef USING_WINDOWS_PLUGIN_DLLS
1017
/*
1018
 * Work-around for Windows plug-in dlls where the plug-in could be
1019
 * using a different HTSlib library to the executable (for example
1020
 * because the latter was build against a static libhts.a).  When this
1021
 * happens, the plug-in can call the wrong copy of hfile_add_scheme_handler().
1022
 * If this is detected, it calls this function which attempts to fix the
1023
 * problem by redirecting to the hfile_add_scheme_handler() in the main
1024
 * executable.
1025
 */
1026
static int try_exe_add_scheme_handler(const char *scheme,
1027
                                      const struct hFILE_scheme_handler *handler)
1028
{
1029
    static void (*add_scheme_handler)(const char *scheme,
1030
                                      const struct hFILE_scheme_handler *handler);
1031
    if (!add_scheme_handler) {
1032
        // dlopen the main executable and resolve hfile_add_scheme_handler
1033
        void *exe_handle = dlopen(NULL, RTLD_LAZY);
1034
        if (!exe_handle) return -1;
1035
        *(void **) (&add_scheme_handler) = dlsym(exe_handle, "hfile_add_scheme_handler");
1036
        dlclose(exe_handle);
1037
    }
1038
    // Check that the symbol was obtained and isn't the one in this copy
1039
    // of the library (to avoid infinite recursion)
1040
    if (!add_scheme_handler || add_scheme_handler == hfile_add_scheme_handler)
1041
        return -1;
1042
    add_scheme_handler(scheme, handler);
1043
    return 0;
1044
}
1045
#else
1046
static int try_exe_add_scheme_handler(const char *scheme,
1047
                                      const struct hFILE_scheme_handler *handler)
1048
0
{
1049
0
    return -1;
1050
0
}
1051
#endif
1052
1053
HTSLIB_EXPORT
1054
void hfile_add_scheme_handler(const char *scheme,
1055
                              const struct hFILE_scheme_handler *handler)
1056
34
{
1057
34
    int absent;
1058
34
    if (handler->open == NULL || handler->isremote == NULL) {
1059
0
        hts_log_warning("Couldn't register scheme handler for %s: missing method", scheme);
1060
0
        return;
1061
0
    }
1062
34
    if (!schemes) {
1063
0
        if (try_exe_add_scheme_handler(scheme, handler) != 0) {
1064
0
            hts_log_warning("Couldn't register scheme handler for %s", scheme);
1065
0
        }
1066
0
        return;
1067
0
    }
1068
34
    khint_t k = kh_put(scheme_string, schemes, scheme, &absent);
1069
34
    if (absent < 0) {
1070
0
        hts_log_warning("Couldn't register scheme handler for %s : %s",
1071
0
                        scheme, strerror(errno));
1072
0
        return;
1073
0
    }
1074
34
    if (absent || priority(handler) > priority(kh_value(schemes, k))) {
1075
33
        kh_value(schemes, k) = handler;
1076
33
    }
1077
34
}
1078
1079
static int init_add_plugin(void *obj, int (*init)(struct hFILE_plugin *),
1080
                           const char *pluginname)
1081
5
{
1082
5
    struct hFILE_plugin_list *p = malloc (sizeof (struct hFILE_plugin_list));
1083
5
    if (p == NULL) {
1084
0
        hts_log_debug("Failed to allocate memory for plugin \"%s\"", pluginname);
1085
0
        return -1;
1086
0
    }
1087
1088
5
    p->plugin.api_version = 1;
1089
5
    p->plugin.obj = obj;
1090
5
    p->plugin.name = NULL;
1091
5
    p->plugin.destroy = NULL;
1092
1093
5
    int ret = (*init)(&p->plugin);
1094
1095
5
    if (ret != 0) {
1096
0
        hts_log_debug("Initialisation failed for plugin \"%s\": %d", pluginname, ret);
1097
0
        free(p);
1098
0
        return ret;
1099
0
    }
1100
1101
5
    hts_log_debug("Loaded \"%s\"", pluginname);
1102
1103
5
    p->next = plugins, plugins = p;
1104
5
    return 0;
1105
5
}
1106
1107
/*
1108
 * Returns 0 on success,
1109
 *        <0 on failure
1110
 */
1111
static int load_hfile_plugins(void)
1112
1
{
1113
1
    static const struct hFILE_scheme_handler
1114
1
        data = { hopen_mem, hfile_always_local, "built-in", 80 },
1115
1
        file = { hopen_fd_fileuri, hfile_always_local, "built-in", 80 },
1116
1
        preload = { hopen_preload, is_preload_url_remote, "built-in", 80 };
1117
1118
1
    schemes = kh_init(scheme_string);
1119
1
    if (schemes == NULL)
1120
0
        return -1;
1121
1122
1
    hfile_add_scheme_handler("data", &data);
1123
1
    hfile_add_scheme_handler("file", &file);
1124
1
    hfile_add_scheme_handler("preload", &preload);
1125
1
    init_add_plugin(NULL, hfile_plugin_init_mem, "mem");
1126
1
    init_add_plugin(NULL, hfile_plugin_init_crypt4gh_needed, "crypt4gh-needed");
1127
1128
#ifdef ENABLE_PLUGINS
1129
    struct hts_path_itr path;
1130
    const char *pluginname;
1131
    hts_path_itr_setup(&path, NULL, NULL, "hfile_", 6, NULL, 0);
1132
    while ((pluginname = hts_path_itr_next(&path)) != NULL) {
1133
        void *obj;
1134
        int (*init)(struct hFILE_plugin *) = (int (*)(struct hFILE_plugin *))
1135
            load_plugin(&obj, pluginname, "hfile_plugin_init");
1136
1137
        if (init) {
1138
            if (init_add_plugin(obj, init, pluginname) != 0)
1139
                close_plugin(obj);
1140
        }
1141
    }
1142
#else
1143
1144
1
#ifdef HAVE_LIBCURL
1145
1
    init_add_plugin(NULL, hfile_plugin_init_libcurl, "libcurl");
1146
1
#endif
1147
1
#ifdef ENABLE_GCS
1148
1
    init_add_plugin(NULL, hfile_plugin_init_gcs, "gcs");
1149
1
#endif
1150
1
#ifdef ENABLE_S3
1151
1
    init_add_plugin(NULL, hfile_plugin_init_s3, "s3");
1152
1
#endif
1153
1154
1
#endif
1155
1156
    // In the unlikely event atexit() fails, it's better to succeed here and
1157
    // carry on; then eventually when the program exits, we'll merely close
1158
    // down the plugins uncleanly, as if we had aborted.
1159
1
    (void) atexit(hfile_exit);
1160
1161
1
    return 0;
1162
1
}
1163
1164
/* A filename like "foo:bar" in which we don't recognise the scheme is
1165
   either an ordinary file or an indication of a missing or broken plugin.
1166
   Try to open it as an ordinary file; but if there's no such file, set
1167
   errno distinctively to make the plugin issue apparent.  */
1168
static hFILE *hopen_unknown_scheme(const char *fname, const char *mode)
1169
49
{
1170
49
    hFILE *fp = hopen_fd(fname, mode);
1171
49
    if (fp == NULL && errno == ENOENT) errno = EPROTONOSUPPORT;
1172
49
    return fp;
1173
49
}
1174
1175
/* Returns the appropriate handler, or NULL if the string isn't an URL.  */
1176
static const struct hFILE_scheme_handler *find_scheme_handler(const char *s)
1177
83.8k
{
1178
83.8k
    static const struct hFILE_scheme_handler unknown_scheme =
1179
83.8k
        { hopen_unknown_scheme, hfile_always_local, "built-in", 0 };
1180
1181
83.8k
    char scheme[12];
1182
83.8k
    int i;
1183
1184
249k
    for (i = 0; i < sizeof scheme; i++)
1185
249k
        if (isalnum_c(s[i]) || s[i] == '+' || s[i] == '-' || s[i] == '.')
1186
166k
            scheme[i] = tolower_c(s[i]);
1187
83.8k
        else if (s[i] == ':') break;
1188
37.2k
        else return NULL;
1189
1190
    // 1 byte schemes are likely windows C:/foo pathnames
1191
46.6k
    if (i <= 1 || i >= sizeof scheme) return NULL;
1192
46.6k
    scheme[i] = '\0';
1193
1194
46.6k
    pthread_mutex_lock(&plugins_lock);
1195
46.6k
    if (!schemes && load_hfile_plugins() < 0) {
1196
0
        pthread_mutex_unlock(&plugins_lock);
1197
0
        return NULL;
1198
0
    }
1199
46.6k
    pthread_mutex_unlock(&plugins_lock);
1200
1201
46.6k
    khint_t k = kh_get(scheme_string, schemes, scheme);
1202
46.6k
    return (k != kh_end(schemes))? kh_value(schemes, k) : &unknown_scheme;
1203
46.6k
}
1204
1205
1206
/***************************
1207
 * Library introspection functions
1208
 ***************************/
1209
1210
/*
1211
 * Fills out sc_list[] with the list of known URL schemes.
1212
 * This can be restricted to just ones from a specific plugin,
1213
 * or all (plugin == NULL).
1214
 *
1215
 * Returns number of schemes found on success;
1216
 *        -1 on failure.
1217
 */
1218
HTSLIB_EXPORT
1219
int hfile_list_schemes(const char *plugin, const char *sc_list[], int *nschemes)
1220
0
{
1221
0
    pthread_mutex_lock(&plugins_lock);
1222
0
    if (!schemes && load_hfile_plugins() < 0) {
1223
0
        pthread_mutex_unlock(&plugins_lock);
1224
0
        return -1;
1225
0
    }
1226
0
    pthread_mutex_unlock(&plugins_lock);
1227
1228
0
    khiter_t k;
1229
0
    int ns = 0;
1230
1231
0
    for (k = kh_begin(schemes); k != kh_end(schemes); k++) {
1232
0
        if (!kh_exist(schemes, k))
1233
0
            continue;
1234
1235
0
        const struct hFILE_scheme_handler *s = kh_value(schemes, k);
1236
0
        if (plugin && strcmp(s->provider, plugin) != 0)
1237
0
            continue;
1238
1239
0
        if (ns < *nschemes)
1240
0
            sc_list[ns] = kh_key(schemes, k);
1241
0
        ns++;
1242
0
    }
1243
1244
0
    if (*nschemes > ns)
1245
0
        *nschemes = ns;
1246
1247
0
    return ns;
1248
0
}
1249
1250
1251
/*
1252
 * Fills out plist[] with the list of known hFILE plugins.
1253
 *
1254
 * Returns number of schemes found on success;
1255
 *        -1 on failure
1256
 */
1257
HTSLIB_EXPORT
1258
int hfile_list_plugins(const char *plist[], int *nplugins)
1259
0
{
1260
0
    pthread_mutex_lock(&plugins_lock);
1261
0
    if (!schemes && load_hfile_plugins() < 0) {
1262
0
        pthread_mutex_unlock(&plugins_lock);
1263
0
        return -1;
1264
0
    }
1265
0
    pthread_mutex_unlock(&plugins_lock);
1266
1267
0
    int np = 0;
1268
0
    if (*nplugins)
1269
0
        plist[np++] = "built-in";
1270
1271
0
    struct hFILE_plugin_list *p = plugins;
1272
0
    while (p) {
1273
0
        if (np < *nplugins)
1274
0
            plist[np] = p->plugin.name;
1275
1276
0
        p = p->next;
1277
0
        np++;
1278
0
    }
1279
1280
0
    if (*nplugins > np)
1281
0
        *nplugins = np;
1282
1283
0
    return np;
1284
0
}
1285
1286
1287
/*
1288
 * Tests for the presence of a specific hFILE plugin.
1289
 *
1290
 * Returns 1 if true
1291
 *         0 otherwise
1292
 */
1293
HTSLIB_EXPORT
1294
int hfile_has_plugin(const char *name)
1295
0
{
1296
0
    pthread_mutex_lock(&plugins_lock);
1297
0
    if (!schemes && load_hfile_plugins() < 0) {
1298
0
        pthread_mutex_unlock(&plugins_lock);
1299
0
        return -1;
1300
0
    }
1301
0
    pthread_mutex_unlock(&plugins_lock);
1302
1303
0
    struct hFILE_plugin_list *p = plugins;
1304
0
    while (p) {
1305
0
        if (strcmp(p->plugin.name, name) == 0)
1306
0
            return 1;
1307
0
        p = p->next;
1308
0
    }
1309
1310
0
    return 0;
1311
0
}
1312
1313
/***************************
1314
 * hFILE interface proper
1315
 ***************************/
1316
1317
hFILE *hopen(const char *fname, const char *mode, ...)
1318
76.6k
{
1319
76.6k
    const struct hFILE_scheme_handler *handler = find_scheme_handler(fname);
1320
76.6k
    if (handler) {
1321
45.4k
        if (strchr(mode, ':') == NULL
1322
44.1k
            || handler->priority < 2000
1323
44.1k
            || handler->vopen == NULL) {
1324
1.35k
            return handler->open(fname, mode);
1325
1.35k
        }
1326
44.1k
        else {
1327
44.1k
            hFILE *fp;
1328
44.1k
            va_list arg;
1329
44.1k
            va_start(arg, mode);
1330
44.1k
            fp = handler->vopen(fname, mode, arg);
1331
44.1k
            va_end(arg);
1332
44.1k
            return fp;
1333
44.1k
        }
1334
45.4k
    }
1335
31.1k
    else if (strcmp(fname, "-") == 0) return hopen_fd_stdinout(mode);
1336
31.1k
    else return hopen_fd(fname, mode);
1337
76.6k
}
1338
1339
HTSLIB_EXPORT
1340
700
int hfile_always_local (const char *fname) { return 0; }
1341
1342
HTSLIB_EXPORT
1343
380
int hfile_always_remote(const char *fname) { return 1; }
1344
1345
int hisremote(const char *fname)
1346
7.23k
{
1347
7.23k
    const struct hFILE_scheme_handler *handler = find_scheme_handler(fname);
1348
7.23k
    return handler? handler->isremote(fname) : 0;
1349
7.23k
}
1350
1351
// Remove an extension, if any, from the basename part of [start,limit).
1352
// Note: Doesn't notice percent-encoded '.' and '/' characters. Don't do that.
1353
static const char *strip_extension(const char *start, const char *limit)
1354
0
{
1355
0
    const char *s = limit;
1356
0
    while (s > start) {
1357
0
        --s;
1358
0
        if (*s == '.') return s;
1359
0
        else if (*s == '/') break;
1360
0
    }
1361
0
    return limit;
1362
0
}
1363
1364
char *haddextension(struct kstring_t *buffer, const char *filename,
1365
                    int replace, const char *new_extension)
1366
0
{
1367
0
    const char *trailing, *end;
1368
1369
0
    if (find_scheme_handler(filename)) {
1370
        // URL, so alter extensions before any trailing query or fragment parts
1371
        // Allow # symbols in s3 URLs
1372
0
        trailing = filename + ((strncmp(filename, "s3://", 5) && strncmp(filename, "s3+http://", 10) && strncmp(filename, "s3+https://", 11))  ? strcspn(filename, "?#") : strcspn(filename, "?"));
1373
0
    }
1374
0
    else {
1375
        // Local path, so alter extensions at the end of the filename
1376
0
        trailing = strchr(filename, '\0');
1377
0
    }
1378
1379
0
    end = replace? strip_extension(filename, trailing) : trailing;
1380
1381
0
    buffer->l = 0;
1382
0
    if (kputsn(filename, end - filename, buffer) >= 0 &&
1383
0
        kputs(new_extension, buffer) >= 0 &&
1384
0
        kputs(trailing, buffer) >= 0) return buffer->s;
1385
0
    else return NULL;
1386
0
}
1387
1388
1389
/*
1390
 * ----------------------------------------------------------------------
1391
 * Minimal stub functions for knet, added after the removal of
1392
 * hfile_net.c and knetfile.c.
1393
 *
1394
 * They exist purely for ABI compatibility, but are simply wrappers to
1395
 * hFILE.  API should be compatible except knet_fileno (unused?).
1396
 *
1397
 * CULL THESE and knetfile.h at the next .so version bump.
1398
 */
1399
typedef struct knetFile_s {
1400
    // As per htslib/knetfile.h.  Duplicated here as we don't wish to
1401
    // have any dependence on the deprecated knetfile.h interface, plus
1402
    // it's hopefully only temporary.
1403
    int type, fd;
1404
    int64_t offset;
1405
    char *host, *port;
1406
    int ctrl_fd, pasv_ip[4], pasv_port, max_response, no_reconnect, is_ready;
1407
    char *response, *retr, *size_cmd;
1408
    int64_t seek_offset;
1409
    int64_t file_size;
1410
    char *path, *http_host;
1411
1412
    // Our local addition
1413
    hFILE *hf;
1414
} knetFile;
1415
1416
HTSLIB_EXPORT
1417
0
knetFile *knet_open(const char *fn, const char *mode) {
1418
0
    knetFile *fp = calloc(1, sizeof(*fp));
1419
0
    if (!fp) return NULL;
1420
0
    if (!(fp->hf = hopen(fn, mode))) {
1421
0
        free(fp);
1422
0
        return NULL;
1423
0
    }
1424
1425
    // FD backend is the only one implementing knet_fileno
1426
0
    fp->fd = fp->hf->backend == &fd_backend
1427
0
        ? ((hFILE_fd *)fp->hf)->fd
1428
0
        : -1;
1429
1430
0
    return fp;
1431
0
}
1432
1433
HTSLIB_EXPORT
1434
0
knetFile *knet_dopen(int fd, const char *mode) {
1435
0
    knetFile *fp = calloc(1, sizeof(*fp));
1436
0
    if (!fp) return NULL;
1437
0
    if (!(fp->hf = hdopen(fd, mode))) {
1438
0
        free(fp);
1439
0
        return NULL;
1440
0
    }
1441
0
    fp->fd = fd;
1442
0
    return fp;
1443
0
}
1444
1445
HTSLIB_EXPORT
1446
0
ssize_t knet_read(knetFile *fp, void *buf, size_t len) {
1447
0
    ssize_t r = hread(fp->hf, buf, len);
1448
0
    fp->offset += r>0?r:0;
1449
0
    return r;
1450
0
}
1451
1452
HTSLIB_EXPORT
1453
0
off_t knet_seek(knetFile *fp, off_t off, int whence) {
1454
0
    off_t r = hseek(fp->hf, off, whence);
1455
0
    if (r >= 0)
1456
0
        fp->offset = r;
1457
0
    return r;
1458
0
}
1459
1460
HTSLIB_EXPORT
1461
0
int knet_close(knetFile *fp) {
1462
0
    int r = hclose(fp->hf);
1463
0
    free(fp);
1464
0
    return r;
1465
0
}