mirror of
https://github.com/lh3/minimap2.git
synced 2026-10-04 01:08:13 +08:00
Merge branch 'master' into avx
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
#### 1. Alignment different with option `-a` or `-c`?
|
||||||
|
|
||||||
|
Without `-a`, `-c` or `--cs`, minimap2 only finds *approximate* mapping
|
||||||
|
locations without detailed base alignment. In particular, the start and end
|
||||||
|
positions of the alignment are impricise. With one of those options, minimap2
|
||||||
|
will perform base alignment, which is generally more accurate but is much
|
||||||
|
slower.
|
||||||
|
|
||||||
|
#### 2. How to map Illumina short reads to noisy long reads?
|
||||||
|
|
||||||
|
No good solutions. The better approach is to assemble short reads into contigs
|
||||||
|
and then map noisy reads to contigs.
|
||||||
|
|
||||||
|
#### 3. The output SAM doesn't have a header.
|
||||||
|
|
||||||
|
By default, minimap2 indexes 4 billion reference bases (4Gb) in a batch and map
|
||||||
|
all reads against each reference batch. Given a reference longer than 4Gb,
|
||||||
|
minimap2 is unable to see all the sequences and thus can't produce a correct
|
||||||
|
SAM header. In this case, minimap2 doesn't output any SAM header. There are two
|
||||||
|
solutions to this issue. First, you may increase option `-I` to, for example,
|
||||||
|
`-I8g` to index more reference bases in a batch. This is preferred if your
|
||||||
|
machine has enough memory. Second, if your machines doesn't have enough memory
|
||||||
|
to hold the reference index, you can use the `--split-prefix` option in a
|
||||||
|
command line like:
|
||||||
|
```sh
|
||||||
|
minimap2 -ax map-ont --split-prefix=tmp ref.fa reads.fq
|
||||||
|
```
|
||||||
|
This second approach uses less memory, but it is slower and requires temporary
|
||||||
|
disk space.
|
||||||
|
|
||||||
|
#### 4. The output SAM is malformatted.
|
||||||
|
|
||||||
|
This typically happens when you use nohup to wrap a minimap2 command line.
|
||||||
|
Nohup is discouraged as it breaks piping. If you have to use nohup, please
|
||||||
|
specify an output file with option `-o`.
|
||||||
|
|
||||||
|
#### 5. How to output one alignment per read?
|
||||||
|
|
||||||
|
You can use `--secondary=no` to suppress secondary alignments (aka multiple
|
||||||
|
mappings), but you can't suppress supplementary alignment (aka split or
|
||||||
|
chimeric alignment) this way. You can use samtools to filter out these
|
||||||
|
alignments:
|
||||||
|
```sh
|
||||||
|
minimap2 -ax map-out ref.fa reads.fq | samtools view -F0x900
|
||||||
|
```
|
||||||
|
However, this is discouraged as supplementary alignment is informative.
|
||||||
@@ -34,6 +34,16 @@ else #if aarch64 is defined
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(asan),)
|
||||||
|
CFLAGS+=-fsanitize=address
|
||||||
|
LIBS+=-fsanitize=address
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(tsan),)
|
||||||
|
CFLAGS+=-fsanitize=thread
|
||||||
|
LIBS+=-fsanitize=thread
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY:all extra clean depend
|
.PHONY:all extra clean depend
|
||||||
.SUFFIXES:.c .o
|
.SUFFIXES:.c .o
|
||||||
|
|
||||||
|
|||||||
@@ -315,9 +315,10 @@ highlighted in bold. The description may help to tune minimap2 parameters.
|
|||||||
### <a name="help"></a>Getting help
|
### <a name="help"></a>Getting help
|
||||||
|
|
||||||
Manpage [minimap2.1][manpage] provides detailed description of minimap2
|
Manpage [minimap2.1][manpage] provides detailed description of minimap2
|
||||||
command line options and optional tags. If you encounter bugs or have further
|
command line options and optional tags. The [FAQ](FAQ.md) page answers several
|
||||||
questions or requests, you can raise an issue at the [issue page][issue].
|
frequently asked questions. If you encounter bugs or have further questions or
|
||||||
There is not a specific mailing list for the time being.
|
requests, you can raise an issue at the [issue page][issue]. There is not a
|
||||||
|
specific mailing list for the time being.
|
||||||
|
|
||||||
### <a name="cite"></a>Citing minimap2
|
### <a name="cite"></a>Citing minimap2
|
||||||
|
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ mm128_t *mm_chain_dp(int max_dist_x, int max_dist_y, int bw, int max_skip, int m
|
|||||||
memcpy(&a[k], &b[w[i].y>>32], n * sizeof(mm128_t));
|
memcpy(&a[k], &b[w[i].y>>32], n * sizeof(mm128_t));
|
||||||
k += n;
|
k += n;
|
||||||
}
|
}
|
||||||
memcpy(u, u2, n_u * 8);
|
if (n_u) memcpy(u, u2, n_u * 8);
|
||||||
memcpy(b, a, k * sizeof(mm128_t)); // write _a_ to _b_ and deallocate _a_ because _a_ is oversized, sometimes a lot
|
if (k) memcpy(b, a, k * sizeof(mm128_t)); // write _a_ to _b_ and deallocate _a_ because _a_ is oversized, sometimes a lot
|
||||||
kfree(km, a); kfree(km, w); kfree(km, u2);
|
kfree(km, a); kfree(km, w); kfree(km, u2);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ int main(int argc, char *argv[])
|
|||||||
while ((mi = mm_idx_reader_read(r, n_threads)) != 0) { // traverse each part of the index
|
while ((mi = mm_idx_reader_read(r, n_threads)) != 0) { // traverse each part of the index
|
||||||
mm_mapopt_update(&mopt, mi); // this sets the maximum minimizer occurrence; TODO: set a better default in mm_mapopt_init()!
|
mm_mapopt_update(&mopt, mi); // this sets the maximum minimizer occurrence; TODO: set a better default in mm_mapopt_init()!
|
||||||
mm_tbuf_t *tbuf = mm_tbuf_init(); // thread buffer; for multi-threading, allocate one tbuf for each thread
|
mm_tbuf_t *tbuf = mm_tbuf_init(); // thread buffer; for multi-threading, allocate one tbuf for each thread
|
||||||
|
gzrewind(f);
|
||||||
|
kseq_rewind(ks);
|
||||||
while (kseq_read(ks) >= 0) { // each kseq_read() call reads one query sequence
|
while (kseq_read(ks) >= 0) { // each kseq_read() call reads one query sequence
|
||||||
mm_reg1_t *reg;
|
mm_reg1_t *reg;
|
||||||
int j, i, n_reg;
|
int j, i, n_reg;
|
||||||
|
|||||||
@@ -79,11 +79,11 @@ static char *mm_escape(char *s)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sam_write_rg_line(kstring_t *str, const char *s)
|
static int sam_write_rg_line(kstring_t *str, const char *s)
|
||||||
{
|
{
|
||||||
char *p, *q, *r, *rg_line = 0;
|
char *p, *q, *r, *rg_line = 0;
|
||||||
memset(mm_rg_id, 0, 256);
|
memset(mm_rg_id, 0, 256);
|
||||||
if (s == 0) return;
|
if (s == 0) return 0;
|
||||||
if (strstr(s, "@RG") != s) {
|
if (strstr(s, "@RG") != s) {
|
||||||
if (mm_verbose >= 1) fprintf(stderr, "[ERROR] the read group line is not started with @RG\n");
|
if (mm_verbose >= 1) fprintf(stderr, "[ERROR] the read group line is not started with @RG\n");
|
||||||
goto err_set_rg;
|
goto err_set_rg;
|
||||||
@@ -108,20 +108,23 @@ static void sam_write_rg_line(kstring_t *str, const char *s)
|
|||||||
for (q = p, r = mm_rg_id; *q && *q != '\t' && *q != '\n'; ++q)
|
for (q = p, r = mm_rg_id; *q && *q != '\t' && *q != '\n'; ++q)
|
||||||
*r++ = *q;
|
*r++ = *q;
|
||||||
mm_sprintf_lite(str, "%s\n", rg_line);
|
mm_sprintf_lite(str, "%s\n", rg_line);
|
||||||
|
return 0;
|
||||||
|
|
||||||
err_set_rg:
|
err_set_rg:
|
||||||
free(rg_line);
|
free(rg_line);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mm_write_sam_hdr(const mm_idx_t *idx, const char *rg, const char *ver, int argc, char *argv[])
|
int mm_write_sam_hdr(const mm_idx_t *idx, const char *rg, const char *ver, int argc, char *argv[])
|
||||||
{
|
{
|
||||||
kstring_t str = {0,0,0};
|
kstring_t str = {0,0,0};
|
||||||
|
int ret = 0;
|
||||||
if (idx) {
|
if (idx) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
for (i = 0; i < idx->n_seq; ++i)
|
for (i = 0; i < idx->n_seq; ++i)
|
||||||
mm_sprintf_lite(&str, "@SQ\tSN:%s\tLN:%d\n", idx->seq[i].name, idx->seq[i].len);
|
mm_sprintf_lite(&str, "@SQ\tSN:%s\tLN:%d\n", idx->seq[i].name, idx->seq[i].len);
|
||||||
}
|
}
|
||||||
if (rg) sam_write_rg_line(&str, rg);
|
if (rg) ret = sam_write_rg_line(&str, rg);
|
||||||
mm_sprintf_lite(&str, "@PG\tID:minimap2\tPN:minimap2");
|
mm_sprintf_lite(&str, "@PG\tID:minimap2\tPN:minimap2");
|
||||||
if (ver) mm_sprintf_lite(&str, "\tVN:%s", ver);
|
if (ver) mm_sprintf_lite(&str, "\tVN:%s", ver);
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
@@ -132,6 +135,7 @@ void mm_write_sam_hdr(const mm_idx_t *idx, const char *rg, const char *ver, int
|
|||||||
}
|
}
|
||||||
mm_err_puts(str.s);
|
mm_err_puts(str.s);
|
||||||
free(str.s);
|
free(str.s);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_cs_core(kstring_t *s, const uint8_t *tseq, const uint8_t *qseq, const mm_reg1_t *r, char *tmp, int no_iden, int write_tag)
|
static void write_cs_core(kstring_t *s, const uint8_t *tseq, const uint8_t *qseq, const mm_reg1_t *r, char *tmp, int no_iden, int write_tag)
|
||||||
|
|||||||
@@ -117,8 +117,8 @@ void mm_idx_stat(const mm_idx_t *mi)
|
|||||||
if (kh_key(h, k)&1) ++n1;
|
if (kh_key(h, k)&1) ++n1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fprintf(stderr, "[M::%s::%.3f*%.2f] distinct minimizers: %d (%.2f%% are singletons); average occurrences: %.3lf; average spacing: %.3lf\n",
|
fprintf(stderr, "[M::%s::%.3f*%.2f] distinct minimizers: %d (%.2f%% are singletons); average occurrences: %.3lf; average spacing: %.3lf; total length: %ld\n",
|
||||||
__func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), n, 100.0*n1/n, (double)sum / n, (double)len / sum);
|
__func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), n, 100.0*n1/n, (double)sum / n, (double)len / sum, (long)len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mm_idx_index_name(mm_idx_t *mi)
|
int mm_idx_index_name(mm_idx_t *mi)
|
||||||
|
|||||||
@@ -18,15 +18,14 @@
|
|||||||
* | | | |
|
* | | | |
|
||||||
* p=p->ptr->ptr->ptr->ptr p->ptr p->ptr->ptr p->ptr->ptr->ptr
|
* p=p->ptr->ptr->ptr->ptr p->ptr p->ptr->ptr p->ptr->ptr->ptr
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define MIN_CORE_SIZE 0x80000
|
|
||||||
|
|
||||||
typedef struct header_t {
|
typedef struct header_t {
|
||||||
size_t size;
|
size_t size;
|
||||||
struct header_t *ptr;
|
struct header_t *ptr;
|
||||||
} header_t;
|
} header_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
void *par;
|
||||||
|
size_t min_core_size;
|
||||||
header_t base, *loop_head, *core_head; /* base is a zero-sized block always kept in the loop */
|
header_t base, *loop_head, *core_head; /* base is a zero-sized block always kept in the loop */
|
||||||
} kmem_t;
|
} kmem_t;
|
||||||
|
|
||||||
@@ -36,31 +35,39 @@ static void panic(const char *s)
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *km_init(void)
|
void *km_init2(void *km_par, size_t min_core_size)
|
||||||
{
|
{
|
||||||
return calloc(1, sizeof(kmem_t));
|
kmem_t *km;
|
||||||
|
km = (kmem_t*)kcalloc(km_par, 1, sizeof(kmem_t));
|
||||||
|
km->par = km_par;
|
||||||
|
km->min_core_size = min_core_size > 0? min_core_size : 0x80000;
|
||||||
|
return (void*)km;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *km_init(void) { return km_init2(0, 0); }
|
||||||
|
|
||||||
void km_destroy(void *_km)
|
void km_destroy(void *_km)
|
||||||
{
|
{
|
||||||
kmem_t *km = (kmem_t*)_km;
|
kmem_t *km = (kmem_t*)_km;
|
||||||
|
void *km_par;
|
||||||
header_t *p, *q;
|
header_t *p, *q;
|
||||||
if (km == NULL) return;
|
if (km == NULL) return;
|
||||||
|
km_par = km->par;
|
||||||
for (p = km->core_head; p != NULL;) {
|
for (p = km->core_head; p != NULL;) {
|
||||||
q = p->ptr;
|
q = p->ptr;
|
||||||
free(p);
|
kfree(km_par, p);
|
||||||
p = q;
|
p = q;
|
||||||
}
|
}
|
||||||
free(km);
|
kfree(km_par, km);
|
||||||
}
|
}
|
||||||
|
|
||||||
static header_t *morecore(kmem_t *km, size_t nu)
|
static header_t *morecore(kmem_t *km, size_t nu)
|
||||||
{
|
{
|
||||||
header_t *q;
|
header_t *q;
|
||||||
size_t bytes, *p;
|
size_t bytes, *p;
|
||||||
nu = (nu + 1 + (MIN_CORE_SIZE - 1)) / MIN_CORE_SIZE * MIN_CORE_SIZE; /* the first +1 for core header */
|
nu = (nu + 1 + (km->min_core_size - 1)) / km->min_core_size * km->min_core_size; /* the first +1 for core header */
|
||||||
bytes = nu * sizeof(header_t);
|
bytes = nu * sizeof(header_t);
|
||||||
q = (header_t*)malloc(bytes);
|
q = (header_t*)kmalloc(km->par, bytes);
|
||||||
if (!q) panic("[morecore] insufficient memory");
|
if (!q) panic("[morecore] insufficient memory");
|
||||||
q->ptr = km->core_head, q->size = nu, km->core_head = q;
|
q->ptr = km->core_head, q->size = nu, km->core_head = q;
|
||||||
p = (size_t*)(q + 1);
|
p = (size_t*)(q + 1);
|
||||||
@@ -125,7 +132,7 @@ void *kmalloc(void *_km, size_t n_bytes)
|
|||||||
|
|
||||||
if (n_bytes == 0) return 0;
|
if (n_bytes == 0) return 0;
|
||||||
if (km == NULL) return malloc(n_bytes);
|
if (km == NULL) return malloc(n_bytes);
|
||||||
n_units = (n_bytes + sizeof(size_t) + sizeof(header_t) - 1) / sizeof(header_t) + 1;
|
n_units = (n_bytes + sizeof(size_t) + sizeof(header_t) - 1) / sizeof(header_t); /* header+n_bytes requires at least this number of units */
|
||||||
|
|
||||||
if (!(q = km->loop_head)) /* the first time when kmalloc() is called, intialize it */
|
if (!(q = km->loop_head)) /* the first time when kmalloc() is called, intialize it */
|
||||||
q = km->loop_head = km->base.ptr = &km->base;
|
q = km->loop_head = km->base.ptr = &km->base;
|
||||||
@@ -160,18 +167,18 @@ void *kcalloc(void *_km, size_t count, size_t size)
|
|||||||
void *krealloc(void *_km, void *ap, size_t n_bytes) // TODO: this can be made more efficient in principle
|
void *krealloc(void *_km, void *ap, size_t n_bytes) // TODO: this can be made more efficient in principle
|
||||||
{
|
{
|
||||||
kmem_t *km = (kmem_t*)_km;
|
kmem_t *km = (kmem_t*)_km;
|
||||||
size_t n_units, *p, *q;
|
size_t cap, *p, *q;
|
||||||
|
|
||||||
if (n_bytes == 0) {
|
if (n_bytes == 0) {
|
||||||
kfree(km, ap); return 0;
|
kfree(km, ap); return 0;
|
||||||
}
|
}
|
||||||
if (km == NULL) return realloc(ap, n_bytes);
|
if (km == NULL) return realloc(ap, n_bytes);
|
||||||
if (ap == NULL) return kmalloc(km, n_bytes);
|
if (ap == NULL) return kmalloc(km, n_bytes);
|
||||||
n_units = (n_bytes + sizeof(size_t) + sizeof(header_t) - 1) / sizeof(header_t);
|
|
||||||
p = (size_t*)ap - 1;
|
p = (size_t*)ap - 1;
|
||||||
if (*p >= n_units) return ap; /* TODO: this prevents shrinking */
|
cap = (*p) * sizeof(header_t) - sizeof(size_t);
|
||||||
|
if (cap >= n_bytes) return ap; /* TODO: this prevents shrinking */
|
||||||
q = (size_t*)kmalloc(km, n_bytes);
|
q = (size_t*)kmalloc(km, n_bytes);
|
||||||
memcpy(q, ap, (*p - 1) * sizeof(header_t));
|
memcpy(q, ap, cap);
|
||||||
kfree(km, ap);
|
kfree(km, ap);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ void *kcalloc(void *km, size_t count, size_t size);
|
|||||||
void kfree(void *km, void *ptr);
|
void kfree(void *km, void *ptr);
|
||||||
|
|
||||||
void *km_init(void);
|
void *km_init(void);
|
||||||
|
void *km_init2(void *km_par, size_t min_core_size);
|
||||||
void km_destroy(void *km);
|
void km_destroy(void *km);
|
||||||
void km_stat(const void *_km, km_stat_t *s);
|
void km_stat(const void *_km, km_stat_t *s);
|
||||||
|
|
||||||
@@ -24,4 +25,13 @@ void km_stat(const void *_km, km_stat_t *s);
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define KMALLOC(km, ptr, len) ((ptr) = (__typeof__(ptr))kmalloc((km), (len) * sizeof(*(ptr))))
|
||||||
|
#define KCALLOC(km, ptr, len) ((ptr) = (__typeof__(ptr))kcalloc((km), (len), sizeof(*(ptr))))
|
||||||
|
#define KREALLOC(km, ptr, len) ((ptr) = (__typeof__(ptr))krealloc((km), (ptr), (len) * sizeof(*(ptr))))
|
||||||
|
|
||||||
|
#define KEXPAND(km, a, m) do { \
|
||||||
|
(m) = (m) >= 4? (m) + ((m)>>1) : 16; \
|
||||||
|
KREALLOC((km), (a), (m)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "bseq.h"
|
#include "bseq.h"
|
||||||
#include "minimap.h"
|
#include "minimap.h"
|
||||||
#include "mmpriv.h"
|
#include "mmpriv.h"
|
||||||
#include "ketopt.h"
|
#include "ketopt.h"
|
||||||
|
|
||||||
#define MM_VERSION "2.17-r943-dirty"
|
#define MM_VERSION "2.17-r963-dirty"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
@@ -172,7 +173,7 @@ int main(int argc, char *argv[])
|
|||||||
else if (c == 'o') {
|
else if (c == 'o') {
|
||||||
if (strcmp(o.arg, "-") != 0) {
|
if (strcmp(o.arg, "-") != 0) {
|
||||||
if (freopen(o.arg, "wb", stdout) == NULL) {
|
if (freopen(o.arg, "wb", stdout) == NULL) {
|
||||||
fprintf(stderr, "[ERROR]\033[1;31m failed to write the output to file '%s'\033[0m\n", o.arg);
|
fprintf(stderr, "[ERROR]\033[1;31m failed to write the output to file '%s'\033[0m: %s\n", o.arg, strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,6 +209,7 @@ int main(int argc, char *argv[])
|
|||||||
else if (c == 337) opt.max_sw_mat = mm_parse_num(o.arg); // --cap-sw-mat
|
else if (c == 337) opt.max_sw_mat = mm_parse_num(o.arg); // --cap-sw-mat
|
||||||
else if (c == 338) opt.max_qlen = mm_parse_num(o.arg); // --max-qlen
|
else if (c == 338) opt.max_qlen = mm_parse_num(o.arg); // --max-qlen
|
||||||
else if (c == 340) junc_bed = o.arg; // --junc-bed
|
else if (c == 340) junc_bed = o.arg; // --junc-bed
|
||||||
|
else if (c == 341) opt.junc_bonus = atoi(o.arg); // --junc-bonus
|
||||||
else if (c == 342) opt.flag |= MM_F_SAM_HIT_ONLY; // --sam-hit-only
|
else if (c == 342) opt.flag |= MM_F_SAM_HIT_ONLY; // --sam-hit-only
|
||||||
else if (c == 314) { // --frag
|
else if (c == 314) { // --frag
|
||||||
yes_or_no(&opt, MM_F_FRAG_MODE, o.longidx, o.arg, 1);
|
yes_or_no(&opt, MM_F_FRAG_MODE, o.longidx, o.arg, 1);
|
||||||
@@ -322,11 +324,11 @@ int main(int argc, char *argv[])
|
|||||||
fprintf(fp_help, " --version show version number\n");
|
fprintf(fp_help, " --version show version number\n");
|
||||||
fprintf(fp_help, " Preset:\n");
|
fprintf(fp_help, " Preset:\n");
|
||||||
fprintf(fp_help, " -x STR preset (always applied before other options; see minimap2.1 for details) []\n");
|
fprintf(fp_help, " -x STR preset (always applied before other options; see minimap2.1 for details) []\n");
|
||||||
fprintf(fp_help, " - map-pb/map-ont: PacBio/Nanopore vs reference mapping\n");
|
fprintf(fp_help, " - map-pb/map-ont - PacBio/Nanopore vs reference mapping\n");
|
||||||
fprintf(fp_help, " - ava-pb/ava-ont: PacBio/Nanopore read overlap\n");
|
fprintf(fp_help, " - ava-pb/ava-ont - PacBio/Nanopore read overlap\n");
|
||||||
fprintf(fp_help, " - asm5/asm10/asm20: asm-to-ref mapping, for ~0.1/1/5%% sequence divergence\n");
|
fprintf(fp_help, " - asm5/asm10/asm20 - asm-to-ref mapping, for ~0.1/1/5%% sequence divergence\n");
|
||||||
fprintf(fp_help, " - splice: long-read spliced alignment\n");
|
fprintf(fp_help, " - splice/splice:hq - long-read/Pacbio-CCS spliced alignment\n");
|
||||||
fprintf(fp_help, " - sr: genomic short-read mapping\n");
|
fprintf(fp_help, " - sr - genomic short-read mapping\n");
|
||||||
fprintf(fp_help, "\nSee `man ./minimap2.1' for detailed description of these and other advanced command-line options.\n");
|
fprintf(fp_help, "\nSee `man ./minimap2.1' for detailed description of these and other advanced command-line options.\n");
|
||||||
return fp_help == stdout? 0 : 1;
|
return fp_help == stdout? 0 : 1;
|
||||||
}
|
}
|
||||||
@@ -337,7 +339,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
idx_rdr = mm_idx_reader_open(argv[o.ind], &ipt, fnw);
|
idx_rdr = mm_idx_reader_open(argv[o.ind], &ipt, fnw);
|
||||||
if (idx_rdr == 0) {
|
if (idx_rdr == 0) {
|
||||||
fprintf(stderr, "[ERROR] failed to open file '%s'\n", argv[o.ind]);
|
fprintf(stderr, "[ERROR] failed to open file '%s': %s\n", argv[o.ind], strerror(errno));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!idx_rdr->is_idx && fnw == 0 && argc - o.ind < 2) {
|
if (!idx_rdr->is_idx && fnw == 0 && argc - o.ind < 2) {
|
||||||
@@ -355,13 +357,19 @@ int main(int argc, char *argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if ((opt.flag & MM_F_OUT_SAM) && idx_rdr->n_parts == 1) {
|
if ((opt.flag & MM_F_OUT_SAM) && idx_rdr->n_parts == 1) {
|
||||||
|
int ret;
|
||||||
if (mm_idx_reader_eof(idx_rdr)) {
|
if (mm_idx_reader_eof(idx_rdr)) {
|
||||||
mm_write_sam_hdr(mi, rg, MM_VERSION, argc, argv);
|
ret = mm_write_sam_hdr(mi, rg, MM_VERSION, argc, argv);
|
||||||
} else {
|
} else {
|
||||||
mm_write_sam_hdr(0, rg, MM_VERSION, argc, argv);
|
ret = mm_write_sam_hdr(0, rg, MM_VERSION, argc, argv);
|
||||||
if (opt.split_prefix == 0 && mm_verbose >= 2)
|
if (opt.split_prefix == 0 && mm_verbose >= 2)
|
||||||
fprintf(stderr, "[WARNING]\033[1;31m For a multi-part index, no @SQ lines will be outputted. Please use --split-prefix.\033[0m\n");
|
fprintf(stderr, "[WARNING]\033[1;31m For a multi-part index, no @SQ lines will be outputted. Please use --split-prefix.\033[0m\n");
|
||||||
}
|
}
|
||||||
|
if (ret != 0) {
|
||||||
|
mm_idx_destroy(mi);
|
||||||
|
mm_idx_reader_close(idx_rdr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mm_verbose >= 3)
|
if (mm_verbose >= 3)
|
||||||
fprintf(stderr, "[M::%s::%.3f*%.2f] loaded/built the index for %d target sequence(s)\n",
|
fprintf(stderr, "[M::%s::%.3f*%.2f] loaded/built the index for %d target sequence(s)\n",
|
||||||
@@ -384,7 +392,7 @@ int main(int argc, char *argv[])
|
|||||||
mm_split_merge(argc - (o.ind + 1), (const char**)&argv[o.ind + 1], &opt, n_parts);
|
mm_split_merge(argc - (o.ind + 1), (const char**)&argv[o.ind + 1], &opt, n_parts);
|
||||||
|
|
||||||
if (fflush(stdout) == EOF) {
|
if (fflush(stdout) == EOF) {
|
||||||
fprintf(stderr, "[ERROR] failed to write the results\n");
|
perror("[ERROR] failed to write the results");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "kthread.h"
|
#include "kthread.h"
|
||||||
#include "kvec.h"
|
#include "kvec.h"
|
||||||
#include "kalloc.h"
|
#include "kalloc.h"
|
||||||
@@ -622,7 +623,7 @@ static mm_bseq_file_t **open_bseqs(int n, const char **fn)
|
|||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
if ((fp[i] = mm_bseq_open(fn[i])) == 0) {
|
if ((fp[i] = mm_bseq_open(fn[i])) == 0) {
|
||||||
if (mm_verbose >= 1)
|
if (mm_verbose >= 1)
|
||||||
fprintf(stderr, "ERROR: failed to open file '%s'\n", fn[i]);
|
fprintf(stderr, "ERROR: failed to open file '%s': %s\n", fn[i], strerror(errno));
|
||||||
for (j = 0; j < i; ++j)
|
for (j = 0; j < i; ++j)
|
||||||
mm_bseq_close(fp[j]);
|
mm_bseq_close(fp[j]);
|
||||||
free(fp);
|
free(fp);
|
||||||
|
|||||||
@@ -638,6 +638,7 @@ s2 i Chaining score of the best secondary chain
|
|||||||
NM i Total number of mismatches and gaps in the alignment
|
NM i Total number of mismatches and gaps in the alignment
|
||||||
MD Z To generate the ref sequence in the alignment
|
MD Z To generate the ref sequence in the alignment
|
||||||
AS i DP alignment score
|
AS i DP alignment score
|
||||||
|
SA Z List of other supplementary alignments
|
||||||
ms i DP score of the max scoring segment in the alignment
|
ms i DP score of the max scoring segment in the alignment
|
||||||
nn i Number of ambiguous bases in the alignment
|
nn i Number of ambiguous bases in the alignment
|
||||||
ts A Transcript strand (splice mode only)
|
ts A Transcript strand (splice mode only)
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ void mm_err_puts(const char *str)
|
|||||||
int ret;
|
int ret;
|
||||||
ret = puts(str);
|
ret = puts(str);
|
||||||
if (ret == EOF) {
|
if (ret == EOF) {
|
||||||
fprintf(stderr, "[ERROR] failed to write the results\n");
|
perror("[ERROR] failed to write the results");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ void mm_err_fwrite(const void *p, size_t size, size_t nitems, FILE *fp)
|
|||||||
int ret;
|
int ret;
|
||||||
ret = fwrite(p, size, nitems, fp);
|
ret = fwrite(p, size, nitems, fp);
|
||||||
if (ret == EOF) {
|
if (ret == EOF) {
|
||||||
fprintf(stderr, "[ERROR] failed to write data\n");
|
perror("[ERROR] failed to write data");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ void mm_err_fread(void *p, size_t size, size_t nitems, FILE *fp)
|
|||||||
int ret;
|
int ret;
|
||||||
ret = fread(p, size, nitems, fp);
|
ret = fread(p, size, nitems, fp);
|
||||||
if (ret == EOF) {
|
if (ret == EOF) {
|
||||||
fprintf(stderr, "[ERROR] failed to read data\n");
|
perror("[ERROR] failed to read data");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-8
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env k8
|
#!/usr/bin/env k8
|
||||||
|
|
||||||
var paftools_version = '2.17-r941';
|
var paftools_version = '2.17-r949-dirty';
|
||||||
|
|
||||||
/*****************************
|
/*****************************
|
||||||
***** Library functions *****
|
***** Library functions *****
|
||||||
@@ -1509,6 +1509,7 @@ function paf_gff2bed(args)
|
|||||||
|
|
||||||
var colors = {
|
var colors = {
|
||||||
'protein_coding':'0,128,255',
|
'protein_coding':'0,128,255',
|
||||||
|
'mRNA':'0,128,255',
|
||||||
'lincRNA':'0,192,0',
|
'lincRNA':'0,192,0',
|
||||||
'snRNA':'0,192,0',
|
'snRNA':'0,192,0',
|
||||||
'miRNA':'0,192,0',
|
'miRNA':'0,192,0',
|
||||||
@@ -1541,8 +1542,8 @@ function paf_gff2bed(args)
|
|||||||
print(a[0][0], st, en, name, 1000, a[0][3], cds_st, cds_en, color, a.length, sizes.join(",") + ",", starts.join(",") + ",");
|
print(a[0][0], st, en, name, 1000, a[0][3], cds_st, cds_en, color, a.length, sizes.join(",") + ",", starts.join(",") + ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
var re_gtf = /(transcript_id|transcript_type|transcript_biotype|gene_name|transcript_name) "([^"]+)";/g;
|
var re_gtf = /\b(transcript_id|transcript_type|transcript_biotype|gene_name|gene_id|gbkey|transcript_name) "([^"]+)";/g;
|
||||||
var re_gff3 = /(transcript_id|transcript_type|transcript_biotype|gene_name|transcript_name)=([^;]+)/g;
|
var re_gff3 = /\b(transcript_id|transcript_type|transcript_biotype|gene_name|gene_id|gbkey|transcript_name)=([^;]+)/g;
|
||||||
var buf = new Bytes();
|
var buf = new Bytes();
|
||||||
var file = args[getopt.ind] == '-'? new File() : new File(args[getopt.ind]);
|
var file = args[getopt.ind] == '-'? new File() : new File(args[getopt.ind]);
|
||||||
|
|
||||||
@@ -1559,19 +1560,19 @@ function paf_gff2bed(args)
|
|||||||
if (t[2] != "CDS" && t[2] != "exon") continue;
|
if (t[2] != "CDS" && t[2] != "exon") continue;
|
||||||
t[3] = parseInt(t[3]) - 1;
|
t[3] = parseInt(t[3]) - 1;
|
||||||
t[4] = parseInt(t[4]);
|
t[4] = parseInt(t[4]);
|
||||||
var id = null, type = "", gname = "N/A", biotype = "", m, tname = "N/A";
|
var id = null, type = "", name = "N/A", biotype = "", m, tname = "N/A";
|
||||||
while ((m = re_gtf.exec(t[8])) != null) {
|
while ((m = re_gtf.exec(t[8])) != null) {
|
||||||
if (m[1] == "transcript_id") id = m[2];
|
if (m[1] == "transcript_id") id = m[2];
|
||||||
else if (m[1] == "transcript_type") type = m[2];
|
else if (m[1] == "transcript_type") type = m[2];
|
||||||
else if (m[1] == "transcript_biotype") biotype = m[2];
|
else if (m[1] == "transcript_biotype" || m[1] == "gbkey") biotype = m[2];
|
||||||
else if (m[1] == "gene_name") name = m[2];
|
else if (m[1] == "gene_name" || m[1] == "gene_id") name = m[2];
|
||||||
else if (m[1] == "transcript_name") tname = m[2];
|
else if (m[1] == "transcript_name") tname = m[2];
|
||||||
}
|
}
|
||||||
while ((m = re_gff3.exec(t[8])) != null) {
|
while ((m = re_gff3.exec(t[8])) != null) {
|
||||||
if (m[1] == "transcript_id") id = m[2];
|
if (m[1] == "transcript_id") id = m[2];
|
||||||
else if (m[1] == "transcript_type") type = m[2];
|
else if (m[1] == "transcript_type") type = m[2];
|
||||||
else if (m[1] == "transcript_biotype") biotype = m[2];
|
else if (m[1] == "transcript_biotype" || m[1] == "gbkey") biotype = m[2];
|
||||||
else if (m[1] == "gene_name") name = m[2];
|
else if (m[1] == "gene_name" || m[1] == "gene_id") name = m[2];
|
||||||
else if (m[1] == "transcript_name") tname = m[2];
|
else if (m[1] == "transcript_name") tname = m[2];
|
||||||
}
|
}
|
||||||
if (type == "" && biotype != "") type = biotype;
|
if (type == "" && biotype != "") type = biotype;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ uint32_t ks_ksmall_uint32_t(size_t n, uint32_t arr[], size_t kk);
|
|||||||
|
|
||||||
void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, int is_hpc, mm128_v *p);
|
void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, int is_hpc, mm128_v *p);
|
||||||
|
|
||||||
void mm_write_sam_hdr(const mm_idx_t *mi, const char *rg, const char *ver, int argc, char *argv[]);
|
int mm_write_sam_hdr(const mm_idx_t *mi, const char *rg, const char *ver, int argc, char *argv[]);
|
||||||
void mm_write_paf(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, void *km, int opt_flag);
|
void mm_write_paf(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, void *km, int opt_flag);
|
||||||
void mm_write_paf3(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, void *km, int opt_flag, int rep_len);
|
void mm_write_paf3(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, void *km, int opt_flag, int rep_len);
|
||||||
void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, int n_regs, const mm_reg1_t *regs);
|
void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, int n_regs, const mm_reg1_t *regs);
|
||||||
|
|||||||
+30
-18
@@ -113,6 +113,7 @@ cdef class Aligner:
|
|||||||
cdef cmappy.mm_mapopt_t map_opt
|
cdef cmappy.mm_mapopt_t map_opt
|
||||||
|
|
||||||
def __cinit__(self, fn_idx_in=None, preset=None, k=None, w=None, min_cnt=None, min_chain_score=None, min_dp_score=None, bw=None, best_n=None, n_threads=3, fn_idx_out=None, max_frag_len=None, extra_flags=None, seq=None, scoring=None):
|
def __cinit__(self, fn_idx_in=None, preset=None, k=None, w=None, min_cnt=None, min_chain_score=None, min_dp_score=None, bw=None, best_n=None, n_threads=3, fn_idx_out=None, max_frag_len=None, extra_flags=None, seq=None, scoring=None):
|
||||||
|
self._idx = NULL
|
||||||
cmappy.mm_set_opt(NULL, &self.idx_opt, &self.map_opt) # set the default options
|
cmappy.mm_set_opt(NULL, &self.idx_opt, &self.map_opt) # set the default options
|
||||||
if preset is not None:
|
if preset is not None:
|
||||||
cmappy.mm_set_opt(str.encode(preset), &self.idx_opt, &self.map_opt) # apply preset
|
cmappy.mm_set_opt(str.encode(preset), &self.idx_opt, &self.map_opt) # apply preset
|
||||||
@@ -170,6 +171,7 @@ cdef class Aligner:
|
|||||||
cdef void *km
|
cdef void *km
|
||||||
cdef cmappy.mm_mapopt_t map_opt
|
cdef cmappy.mm_mapopt_t map_opt
|
||||||
|
|
||||||
|
if self._idx == NULL: return
|
||||||
map_opt = self.map_opt
|
map_opt = self.map_opt
|
||||||
if max_frag_len is not None: map_opt.max_frag_len = max_frag_len
|
if max_frag_len is not None: map_opt.max_frag_len = max_frag_len
|
||||||
if extra_flags is not None: map_opt.flag |= extra_flags
|
if extra_flags is not None: map_opt.flag |= extra_flags
|
||||||
@@ -186,27 +188,36 @@ cdef class Aligner:
|
|||||||
_seq2 = seq2 if isinstance(seq2, bytes) else seq2.encode()
|
_seq2 = seq2 if isinstance(seq2, bytes) else seq2.encode()
|
||||||
regs = cmappy.mm_map_aux(self._idx, _seq, _seq2, &n_regs, b._b, &map_opt)
|
regs = cmappy.mm_map_aux(self._idx, _seq, _seq2, &n_regs, b._b, &map_opt)
|
||||||
|
|
||||||
for i in range(n_regs):
|
try:
|
||||||
cmappy.mm_reg2hitpy(self._idx, ®s[i], &h)
|
i = 0
|
||||||
cigar, _cs, _MD = [], '', ''
|
while i < n_regs:
|
||||||
for k in range(h.n_cigar32): # convert the 32-bit CIGAR encoding to Python array
|
cmappy.mm_reg2hitpy(self._idx, ®s[i], &h)
|
||||||
c = h.cigar32[k]
|
cigar, _cs, _MD = [], '', ''
|
||||||
cigar.append([c>>4, c&0xf])
|
for k in range(h.n_cigar32): # convert the 32-bit CIGAR encoding to Python array
|
||||||
if cs or MD: # generate the cs and/or the MD tag, if requested
|
c = h.cigar32[k]
|
||||||
if cs:
|
cigar.append([c>>4, c&0xf])
|
||||||
l_cs_str = cmappy.mm_gen_cs(km, &cs_str, &m_cs_str, self._idx, ®s[i], _seq, 1)
|
if cs or MD: # generate the cs and/or the MD tag, if requested
|
||||||
_cs = cs_str[:l_cs_str] if isinstance(cs_str, str) else cs_str[:l_cs_str].decode()
|
if cs:
|
||||||
if MD:
|
l_cs_str = cmappy.mm_gen_cs(km, &cs_str, &m_cs_str, self._idx, ®s[i], _seq, 1)
|
||||||
l_cs_str = cmappy.mm_gen_MD(km, &cs_str, &m_cs_str, self._idx, ®s[i], _seq)
|
_cs = cs_str[:l_cs_str] if isinstance(cs_str, str) else cs_str[:l_cs_str].decode()
|
||||||
_MD = cs_str[:l_cs_str] if isinstance(cs_str, str) else cs_str[:l_cs_str].decode()
|
if MD:
|
||||||
yield Alignment(h.ctg, h.ctg_len, h.ctg_start, h.ctg_end, h.strand, h.qry_start, h.qry_end, h.mapq, cigar, h.is_primary, h.mlen, h.blen, h.NM, h.trans_strand, h.seg_id, _cs, _MD)
|
l_cs_str = cmappy.mm_gen_MD(km, &cs_str, &m_cs_str, self._idx, ®s[i], _seq)
|
||||||
cmappy.mm_free_reg1(®s[i])
|
_MD = cs_str[:l_cs_str] if isinstance(cs_str, str) else cs_str[:l_cs_str].decode()
|
||||||
free(regs)
|
yield Alignment(h.ctg, h.ctg_len, h.ctg_start, h.ctg_end, h.strand, h.qry_start, h.qry_end, h.mapq, cigar, h.is_primary, h.mlen, h.blen, h.NM, h.trans_strand, h.seg_id, _cs, _MD)
|
||||||
free(cs_str)
|
cmappy.mm_free_reg1(®s[i])
|
||||||
|
i += 1
|
||||||
|
finally:
|
||||||
|
while i < n_regs:
|
||||||
|
cmappy.mm_free_reg1(®s[i])
|
||||||
|
i += 1
|
||||||
|
free(regs)
|
||||||
|
free(cs_str)
|
||||||
|
|
||||||
def seq(self, str name, int start=0, int end=0x7fffffff):
|
def seq(self, str name, int start=0, int end=0x7fffffff):
|
||||||
cdef int l
|
cdef int l
|
||||||
cdef char *s = cmappy.mappy_fetch_seq(self._idx, name.encode(), start, end, &l)
|
cdef char *s
|
||||||
|
if self._idx == NULL: return
|
||||||
|
s = cmappy.mappy_fetch_seq(self._idx, name.encode(), start, end, &l)
|
||||||
if l == 0: return None
|
if l == 0: return None
|
||||||
r = s[:l] if isinstance(s, str) else s[:l].decode()
|
r = s[:l] if isinstance(s, str) else s[:l].decode()
|
||||||
free(s)
|
free(s)
|
||||||
@@ -224,6 +235,7 @@ cdef class Aligner:
|
|||||||
@property
|
@property
|
||||||
def seq_names(self):
|
def seq_names(self):
|
||||||
cdef char *p
|
cdef char *p
|
||||||
|
if self._idx == NULL: return
|
||||||
sn = []
|
sn = []
|
||||||
for i in range(self._idx.n_seq):
|
for i in range(self._idx.n_seq):
|
||||||
p = self._idx.seq[i].name
|
p = self._idx.seq[i].name
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "mmpriv.h"
|
#include "mmpriv.h"
|
||||||
|
|
||||||
FILE *mm_split_init(const char *prefix, const mm_idx_t *mi)
|
FILE *mm_split_init(const char *prefix, const mm_idx_t *mi)
|
||||||
@@ -13,7 +14,7 @@ FILE *mm_split_init(const char *prefix, const mm_idx_t *mi)
|
|||||||
sprintf(fn, "%s.%.4d.tmp", prefix, mi->index);
|
sprintf(fn, "%s.%.4d.tmp", prefix, mi->index);
|
||||||
if ((fp = fopen(fn, "wb")) == NULL) {
|
if ((fp = fopen(fn, "wb")) == NULL) {
|
||||||
if (mm_verbose >= 1)
|
if (mm_verbose >= 1)
|
||||||
fprintf(stderr, "[ERROR]\033[1;31m failed to write to temporary file '%s'\033[0m\n", fn);
|
fprintf(stderr, "[ERROR]\033[1;31m failed to write to temporary file '%s'\033[0m: %s\n", fn, strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
mm_err_fwrite(&k, 4, 1, fp);
|
mm_err_fwrite(&k, 4, 1, fp);
|
||||||
@@ -41,7 +42,7 @@ mm_idx_t *mm_split_merge_prep(const char *prefix, int n_splits, FILE **fp, uint3
|
|||||||
sprintf(fn, "%s.%.4d.tmp", prefix, i);
|
sprintf(fn, "%s.%.4d.tmp", prefix, i);
|
||||||
if ((fp[i] = fopen(fn, "rb")) == 0) {
|
if ((fp[i] = fopen(fn, "rb")) == 0) {
|
||||||
if (mm_verbose >= 1)
|
if (mm_verbose >= 1)
|
||||||
fprintf(stderr, "ERROR: failed to open temporary file '%s'\n", fn);
|
fprintf(stderr, "ERROR: failed to open temporary file '%s': %s\n", fn, strerror(errno));
|
||||||
for (j = 0; j < i; ++j)
|
for (j = 0; j < i; ++j)
|
||||||
fclose(fp[j]);
|
fclose(fp[j]);
|
||||||
free(fn);
|
free(fn);
|
||||||
|
|||||||
Reference in New Issue
Block a user