int bam_smpl_get_sample_id(bam_smpl_t *bsmpl, int bam_id, bam1_t *bam_rec) { file_t *file = &bsmpl->files[bam_id]; if ( file->default_idx >= 0 ) return file->default_idx; char *aux_rg = (char*) bam_aux_get(bam_rec, "RG"); aux_rg = aux_rg ? aux_rg+1 : "?"; int rg_id; if ( khash_str2int_get(file->rg2idx, aux_rg, &rg_id)==0 ) return rg_id; if ( khash_str2int_get(file->rg2idx, "?", &rg_id)==0 ) return rg_id; return -1; }
static int mplp_func(void *data, bam1_t *b) { extern int bam_realn(bam1_t *b, const char *ref); extern int bam_prob_realn_core(bam1_t *b, const char *ref, int ref_len, int flag); extern int bam_cap_mapQ(bam1_t *b, char *ref, int ref_len, int thres); char *ref; mplp_aux_t *ma = (mplp_aux_t*)data; int ret, skip = 0, ref_len; do { int has_ref; ret = ma->iter? sam_itr_next(ma->fp, ma->iter, b) : sam_read1(ma->fp, ma->h, b); if (ret < 0) break; // The 'B' cigar operation is not part of the specification, considering as obsolete. // bam_remove_B(b); if (b->core.tid < 0 || (b->core.flag&BAM_FUNMAP)) { // exclude unmapped reads skip = 1; continue; } if (ma->conf->rflag_require && !(ma->conf->rflag_require&b->core.flag)) { skip = 1; continue; } if (ma->conf->rflag_filter && ma->conf->rflag_filter&b->core.flag) { skip = 1; continue; } if (ma->conf->bed) { // test overlap skip = !bed_overlap(ma->conf->bed, ma->h->target_name[b->core.tid], b->core.pos, bam_endpos(b)); if (skip) continue; } if (ma->conf->rghash) { // exclude read groups uint8_t *rg = bam_aux_get(b, "RG"); skip = (rg && khash_str2int_get(ma->conf->rghash, (const char*)(rg+1), NULL)==0); if (skip) continue; } if (ma->conf->flag & MPLP_ILLUMINA13) { int i; uint8_t *qual = bam_get_qual(b); for (i = 0; i < b->core.l_qseq; ++i) qual[i] = qual[i] > 31? qual[i] - 31 : 0; } if (ma->conf->fai && b->core.tid >= 0) { has_ref = mplp_get_ref(ma, b->core.tid, &ref, &ref_len); if (has_ref && ref_len <= b->core.pos) { // exclude reads outside of the reference sequence fprintf(stderr,"[%s] Skipping because %d is outside of %d [ref:%d]\n", __func__, b->core.pos, ref_len, b->core.tid); skip = 1; continue; } } else { has_ref = 0; } skip = 0; if (has_ref && (ma->conf->flag&MPLP_REALN)) bam_prob_realn_core(b, ref, ref_len, (ma->conf->flag & MPLP_REDO_BAQ)? 7 : 3); if (has_ref && ma->conf->capQ_thres > 10) { int q = bam_cap_mapQ(b, ref, ref_len, ma->conf->capQ_thres); if (q < 0) skip = 1; else if (b->core.qual > q) b->core.qual = q; } if (b->core.qual < ma->conf->min_mq) skip = 1; else if ((ma->conf->flag&MPLP_NO_ORPHAN) && (b->core.flag&BAM_FPAIRED) && !(b->core.flag&BAM_FPROPER_PAIR)) skip = 1; } while (skip); return ret; }
// Add a new region into a list sorted by start,end. On input the coordinates // are 1-based, stored 0-based, inclusive. static void _regions_add(bcf_sr_regions_t *reg, const char *chr, int start, int end) { if ( start==-1 && end==-1 ) { start = 0; end = MAX_CSI_COOR-1; } else { start--; end--; // store 0-based coordinates } if ( !reg->seq_hash ) reg->seq_hash = khash_str2int_init(); int iseq; if ( khash_str2int_get(reg->seq_hash, chr, &iseq)<0 ) { // the chromosome block does not exist iseq = reg->nseqs++; reg->seq_names = (char**) realloc(reg->seq_names,sizeof(char*)*reg->nseqs); reg->regs = (region_t*) realloc(reg->regs,sizeof(region_t)*reg->nseqs); memset(®->regs[reg->nseqs-1],0,sizeof(region_t)); reg->seq_names[iseq] = strdup(chr); reg->regs[iseq].creg = -1; khash_str2int_set(reg->seq_hash,reg->seq_names[iseq],iseq); } region_t *creg = ®->regs[iseq]; // the regions may not be sorted on input: binary search int i, min = 0, max = creg->nregs - 1; while ( min<=max ) { i = (max+min)/2; if ( start < creg->regs[i].start ) max = i - 1; else if ( start > creg->regs[i].start ) min = i + 1; else break; } if ( min>max || creg->regs[i].start!=start || creg->regs[i].end!=end ) { // no such region, insert a new one just after max hts_expand(region1_t,creg->nregs+1,creg->mregs,creg->regs); if ( ++max < creg->nregs ) memmove(&creg->regs[max+1],&creg->regs[max],(creg->nregs - max)*sizeof(region1_t)); creg->regs[max].start = start; creg->regs[max].end = end; creg->nregs++; } }
int bcf_sr_regions_seek(bcf_sr_regions_t *reg, const char *seq) { reg->iseq = reg->start = reg->end = -1; if ( khash_str2int_get(reg->seq_hash, seq, ®->iseq) < 0 ) return -1; // sequence seq not in regions // using in-memory regions if ( reg->regs ) return 0; // reading regions from tabix if ( reg->itr ) tbx_itr_destroy(reg->itr); reg->itr = tbx_itr_querys(reg->tbx, seq); if ( reg->itr ) return 0; return -1; }
int ploidy_parse(const char *line, char **chr_beg, char **chr_end, reg_t *reg, void *payload, void *usr) { int i, ret; ploidy_t *ploidy = (ploidy_t*) usr; void *sex2id = ploidy->sex2id; // Check for special case of default ploidy "* * * <sex> <ploidy>" int default_ploidy_def = 0; char *ss = (char*) line; while ( *ss && isspace(*ss) ) ss++; if ( ss[0]=='*' && (!ss[1] || isspace(ss[1])) ) default_ploidy_def = 1; // definition of default ploidy, chr="*" else { // Fill CHR,FROM,TO ret = regidx_parse_tab(line,chr_beg,chr_end,reg,NULL,NULL); if ( ret!=0 ) return ret; } // Skip the fields already parsed by regidx_parse_tab ss = (char*) line; while ( *ss && isspace(*ss) ) ss++; for (i=0; i<3; i++) { while ( *ss && !isspace(*ss) ) ss++; if ( !*ss ) return -2; // wrong number of fields while ( *ss && isspace(*ss) ) ss++; } if ( !*ss ) return -2; // Parse the payload char *se = ss; while ( *se && !isspace(*se) ) se++; if ( !*se || se==ss ) error("Could not parse: %s\n", line); ploidy->tmp_str.l = 0; kputsn(ss,se-ss,&ploidy->tmp_str); sex_ploidy_t *sp = (sex_ploidy_t*) payload; if ( khash_str2int_get(sex2id, ploidy->tmp_str.s, &sp->sex) != 0 ) { ploidy->nsex++; hts_expand0(char*,ploidy->nsex,ploidy->msex,ploidy->id2sex); ploidy->id2sex[ploidy->nsex-1] = strdup(ploidy->tmp_str.s); sp->sex = khash_str2int_inc(ploidy->sex2id, ploidy->id2sex[ploidy->nsex-1]); ploidy->sex2dflt = (int*) realloc(ploidy->sex2dflt,sizeof(int)*ploidy->nsex); ploidy->sex2dflt[ploidy->nsex-1] = ploidy->dflt; }
inline int regidx_push(regidx_t *idx, char *chr_beg, char *chr_end, uint32_t beg, uint32_t end, void *payload) { if ( beg > MAX_COOR_0 ) beg = MAX_COOR_0; if ( end > MAX_COOR_0 ) end = MAX_COOR_0; int rid; idx->str.l = 0; kputsn(chr_beg, chr_end-chr_beg+1, &idx->str); if ( khash_str2int_get(idx->seq2regs, idx->str.s, &rid)!=0 ) { // new chromosome idx->nseq++; int m_prev = idx->mseq; hts_expand0(reglist_t,idx->nseq,idx->mseq,idx->seq); hts_expand0(char*,idx->nseq,m_prev,idx->seq_names); idx->seq_names[idx->nseq-1] = strdup(idx->str.s); rid = khash_str2int_inc(idx->seq2regs, idx->seq_names[idx->nseq-1]); }
int regidx_insert(regidx_t *idx, char *line) { if ( !line ) return _regidx_build_index(idx); char *chr_from, *chr_to; reg_t reg; int ret = idx->parse(line,&chr_from,&chr_to,®,idx->payload,idx->usr); if ( ret==-2 ) return -1; // error if ( ret==-1 ) return 0; // skip the line int rid; idx->str.l = 0; kputsn(chr_from, chr_to-chr_from+1, &idx->str); if ( khash_str2int_get(idx->seq2regs, idx->str.s, &rid)!=0 ) { idx->nseq++; int m_prev = idx->mseq; hts_expand0(reglist_t,idx->nseq,idx->mseq,idx->seq); hts_expand0(char*,idx->nseq,m_prev,idx->seq_names); idx->seq_names[idx->nseq-1] = strdup(idx->str.s); rid = khash_str2int_inc(idx->seq2regs, idx->seq_names[idx->nseq-1]); }
static void bsmpl_add_readgroup(bam_smpl_t *bsmpl, file_t *file, const char *rg_id, const char *smpl_name) { int ismpl = -1; if ( smpl_name ) { if ( khash_str2int_get(bsmpl->name2idx,smpl_name,&ismpl) < 0 ) { // new sample bsmpl->nsmpl++; bsmpl->smpl = (char**) realloc(bsmpl->smpl,sizeof(char*)*bsmpl->nsmpl); bsmpl->smpl[bsmpl->nsmpl-1] = strdup(smpl_name); ismpl = khash_str2int_inc(bsmpl->name2idx,bsmpl->smpl[bsmpl->nsmpl-1]); } } if ( !strcmp("*",rg_id) ) { // all read groups in the bam treated as the same sample file->default_idx = ismpl; return; } if ( !file->rg2idx ) file->rg2idx = khash_str2int_init(); if ( khash_str2int_has_key(file->rg2idx,rg_id) ) return; // duplicate @RG:ID khash_str2int_set(file->rg2idx, strdup(rg_id), ismpl); }
int regidx_seq_nregs(regidx_t *idx, const char *seq) { int iseq; if ( khash_str2int_get(idx->seq2regs, seq, &iseq)!=0 ) return 0; // no such sequence return idx->seq[iseq].nreg; }
static void bcf_sr_sort_set(bcf_srs_t *readers, sr_sort_t *srt, const char *chr, int min_pos) { if ( !srt->grp_str2int ) { // first time here, initialize if ( !srt->pair ) { if ( readers->collapse==COLLAPSE_NONE ) readers->collapse = BCF_SR_PAIR_EXACT; bcf_sr_set_opt(readers, BCF_SR_PAIR_LOGIC, readers->collapse); } bcf_sr_init_scores(srt); srt->grp_str2int = khash_str2int_init(); srt->var_str2int = khash_str2int_init(); } int k; khash_t(str2int) *hash; hash = srt->grp_str2int; for (k=0; k < kh_end(hash); k++) if ( kh_exist(hash,k) ) free((char*)kh_key(hash,k)); hash = srt->var_str2int; for (k=0; k < kh_end(hash); k++) if ( kh_exist(hash,k) ) free((char*)kh_key(hash,k)); kh_clear(str2int, srt->grp_str2int); kh_clear(str2int, srt->var_str2int); srt->ngrp = srt->nvar = srt->nvset = 0; grp_t grp; memset(&grp,0,sizeof(grp_t)); // group VCFs into groups, each with a unique combination of variants in the duplicate lines int ireader,ivar,irec,igrp,ivset,iact; for (ireader=0; ireader<readers->nreaders; ireader++) srt->vcf_buf[ireader].nrec = 0; for (iact=0; iact<srt->nactive; iact++) { ireader = srt->active[iact]; bcf_sr_t *reader = &readers->readers[ireader]; int rid = bcf_hdr_name2id(reader->header, chr); grp.nvar = 0; hts_expand(int,reader->nbuffer,srt->moff,srt->off); srt->noff = 0; srt->str.l = 0; for (irec=1; irec<=reader->nbuffer; irec++) { bcf1_t *line = reader->buffer[irec]; if ( line->rid!=rid || line->pos!=min_pos ) break; if ( srt->str.l ) kputc(';',&srt->str); srt->off[srt->noff++] = srt->str.l; size_t beg = srt->str.l; for (ivar=1; ivar<line->n_allele; ivar++) { if ( ivar>1 ) kputc(',',&srt->str); kputs(line->d.allele[0],&srt->str); kputc('>',&srt->str); kputs(line->d.allele[ivar],&srt->str); } if ( line->n_allele==1 ) { kputs(line->d.allele[0],&srt->str); kputsn(">.",2,&srt->str); } // Create new variant or attach to existing one. But careful, there can be duplicate // records with the same POS,REF,ALT (e.g. in dbSNP-b142) char *var_str = beg + srt->str.s; int ret, var_idx = 0, var_end = srt->str.l; while ( 1 ) { ret = khash_str2int_get(srt->var_str2int, var_str, &ivar); if ( ret==-1 ) break; var_t *var = &srt->var[ivar]; if ( var->vcf[var->nvcf-1] != ireader ) break; srt->str.l = var_end; kputw(var_idx, &srt->str); var_str = beg + srt->str.s; var_idx++; } if ( ret==-1 ) { ivar = srt->nvar++; hts_expand0(var_t,srt->nvar,srt->mvar,srt->var); srt->var[ivar].nvcf = 0; khash_str2int_set(srt->var_str2int, strdup(var_str), ivar); free(srt->var[ivar].str); // possible left-over from the previous position } var_t *var = &srt->var[ivar]; var->nalt = line->n_allele - 1; var->type = bcf_get_variant_types(line); srt->str.s[var_end] = 0; if ( ret==-1 ) var->str = strdup(var_str); int mvcf = var->mvcf; var->nvcf++; hts_expand0(int*, var->nvcf, var->mvcf, var->vcf); if ( mvcf != var->mvcf ) var->rec = (bcf1_t **) realloc(var->rec,sizeof(bcf1_t*)*var->mvcf); var->vcf[var->nvcf-1] = ireader; var->rec[var->nvcf-1] = line; grp.nvar++; hts_expand(var_t,grp.nvar,grp.mvar,grp.var); grp.var[grp.nvar-1] = ivar; } char *grp_key = grp_create_key(srt); int ret = khash_str2int_get(srt->grp_str2int, grp_key, &igrp); if ( ret==-1 ) { igrp = srt->ngrp++; hts_expand0(grp_t, srt->ngrp, srt->mgrp, srt->grp); free(srt->grp[igrp].var); srt->grp[igrp] = grp; srt->grp[igrp].key = grp_key; khash_str2int_set(srt->grp_str2int, grp_key, igrp); memset(&grp,0,sizeof(grp_t)); } else free(grp_key); srt->grp[igrp].nvcf++; } free(grp.var); // initialize bitmask - which groups is the variant present in for (ivar=0; ivar<srt->nvar; ivar++) { srt->var[ivar].mask = kbs_resize(srt->var[ivar].mask, srt->ngrp); kbs_clear(srt->var[ivar].mask); } for (igrp=0; igrp<srt->ngrp; igrp++) { for (ivar=0; ivar<srt->grp[igrp].nvar; ivar++) { int i = srt->grp[igrp].var[ivar]; kbs_insert(srt->var[i].mask, igrp); } } // create the initial list of variant sets for (ivar=0; ivar<srt->nvar; ivar++) { ivset = srt->nvset++; hts_expand0(varset_t, srt->nvset, srt->mvset, srt->vset); varset_t *vset = &srt->vset[ivset]; vset->nvar = 1; hts_expand0(var_t, vset->nvar, vset->mvar, vset->var); vset->var[vset->nvar-1] = ivar; var_t *var = &srt->var[ivar]; vset->cnt = var->nvcf; vset->mask = kbs_resize(vset->mask, srt->ngrp); kbs_clear(vset->mask); kbs_bitwise_or(vset->mask, var->mask); int type = 0; if ( var->type==VCF_REF ) type |= SR_REF; else { if ( var->type & VCF_SNP ) type |= SR_SNP; if ( var->type & VCF_MNP ) type |= SR_SNP; if ( var->type & VCF_INDEL ) type |= SR_INDEL; if ( var->type & VCF_OTHER ) type |= SR_OTHER; } var->type = type; } #if DEBUG_VSETS debug_vsets(srt); #endif // initialize the pairing matrix hts_expand(int, srt->ngrp*srt->nvset, srt->mpmat, srt->pmat); hts_expand(int, srt->nvset, srt->mcnt, srt->cnt); memset(srt->pmat, 0, sizeof(*srt->pmat)*srt->ngrp*srt->nvset); for (ivset=0; ivset<srt->nvset; ivset++) { varset_t *vset = &srt->vset[ivset]; for (igrp=0; igrp<srt->ngrp; igrp++) srt->pmat[ivset*srt->ngrp+igrp] = 0; srt->cnt[ivset] = vset->cnt; } // pair the lines while ( srt->nvset ) { #if DEBUG_VSETS fprintf(stderr,"\n"); debug_vsets(srt); #endif int imax = 0; for (ivset=1; ivset<srt->nvset; ivset++) if ( srt->cnt[imax] < srt->cnt[ivset] ) imax = ivset; int ipair = -1; uint32_t max_score = 0; for (ivset=0; ivset<srt->nvset; ivset++) { if ( kbs_logical_and(srt->vset[imax].mask,srt->vset[ivset].mask) ) continue; // cannot be merged uint32_t score = pairing_score(srt, imax, ivset); // fprintf(stderr,"score: %d %d, logic=%d \t..\t %u\n", imax,ivset,srt->pair,score); if ( max_score < score ) { max_score = score; ipair = ivset; } } // merge rows creating a new variant set this way if ( ipair!=-1 && ipair!=imax ) { imax = merge_vsets(srt, imax, ipair); continue; } push_vset(srt, imax); } srt->chr = chr; srt->pos = min_pos; }
int bcf_sr_regions_next(bcf_sr_regions_t *reg) { if ( reg->iseq<0 ) return -1; reg->start = reg->end = -1; reg->nals = 0; // using in-memory regions if ( reg->regs ) { while ( reg->iseq < reg->nseqs ) { reg->regs[reg->iseq].creg++; if ( reg->regs[reg->iseq].creg < reg->regs[reg->iseq].nregs ) break; reg->iseq++; } if ( reg->iseq >= reg->nseqs ) { reg->iseq = -1; return -1; } // no more regions left region1_t *creg = ®->regs[reg->iseq].regs[reg->regs[reg->iseq].creg]; reg->start = creg->start; reg->end = creg->end; return 0; } // reading from tabix char *chr, *chr_end; int ichr = 0, ifrom = 1, ito = 2, is_bed = 0, from, to; if ( reg->tbx ) { ichr = reg->tbx->conf.sc-1; ifrom = reg->tbx->conf.bc-1; ito = reg->tbx->conf.ec-1; if ( ito<0 ) ito = ifrom; is_bed = reg->tbx->conf.preset==TBX_UCSC ? 1 : 0; } int ret = 0; while ( !ret ) { if ( reg->itr ) { // tabix index present, reading a chromosome block ret = tbx_itr_next(reg->file, reg->tbx, reg->itr, ®->line); if ( ret<0 ) { reg->iseq = -1; return -1; } } else { if ( reg->is_bin ) { // Waited for seek which never came. Reopen in text mode and stream // through the regions, otherwise hts_getline would fail hts_close(reg->file); reg->file = hts_open(reg->fname, "r"); if ( !reg->file ) { fprintf(stderr,"[%s:%d %s] Could not open file: %s\n", __FILE__,__LINE__,__FUNCTION__,reg->fname); reg->file = NULL; bcf_sr_regions_destroy(reg); return -1; } reg->is_bin = 0; } // tabix index absent, reading the whole file ret = hts_getline(reg->file, KS_SEP_LINE, ®->line); if ( ret<0 ) { reg->iseq = -1; return -1; } } ret = _regions_parse_line(reg->line.s, ichr,ifrom,ito, &chr,&chr_end,&from,&to); if ( ret<0 ) { fprintf(stderr,"[%s:%d] Could not parse the file %s, using the columns %d,%d,%d\n", __FILE__,__LINE__,reg->fname,ichr+1,ifrom+1,ito+1); return -1; } } if ( is_bed ) from++; *chr_end = 0; if ( khash_str2int_get(reg->seq_hash, chr, ®->iseq)<0 ) { fprintf(stderr,"Broken tabix index? The sequence \"%s\" not in dictionary [%s]\n", chr,reg->line.s); exit(1); } *chr_end = '\t'; reg->start = from - 1; reg->end = to - 1; return 0; }
/* The logic of this function is a bit complicated because we want to work also with broken bams containing read groups that are not listed in the header. The desired behavior is as follows: - when -G is given, read groups which are not listed in the header must be given explicitly using the "?" symbol in -G. Otherwise: - if the bam has no header, all reads in the file are assigned to a single sample named after the file - if there is at least one sample defined in the header, reads with no read group id or with a read group id not listed in the header are assigned to the first sample encountered in the header */ int bam_smpl_add_bam(bam_smpl_t *bsmpl, char *bam_hdr, const char *fname) { bsmpl->nfiles++; bsmpl->files = (file_t*) realloc(bsmpl->files,bsmpl->nfiles*sizeof(file_t)); file_t *file = &bsmpl->files[bsmpl->nfiles-1]; memset(file,0,sizeof(file_t)); file->fname = strdup(fname); file->default_idx = -1; if ( bsmpl->ignore_rg || !bam_hdr ) { // The option --ignore-RG is set or there is no BAM header: use the file name as the sample name bsmpl_add_readgroup(bsmpl,file,"*",file->fname); return bsmpl->nfiles-1; } void *bam_smpls = khash_str2int_init(); int first_smpl = -1, nskipped = 0; const char *p = bam_hdr, *q, *r; while (p != NULL && (q = strstr(p, "@RG")) != 0) { char *eol = strchr(q + 3, '\n'); if (q > bam_hdr && *(q - 1) != '\n') { // @RG must be at start of line p = eol; continue; } p = q + 3; if ((q = strstr(p, "\tID:")) != 0) q += 4; if ((r = strstr(p, "\tSM:")) != 0) r += 4; if (r && q) { char *u, *v; int ioq, ior; for (u = (char*)q; *u && *u != '\t' && *u != '\n'; ++u); for (v = (char*)r; *v && *v != '\t' && *v != '\n'; ++v); ioq = *u; ior = *v; *u = *v = '\0'; // q now points to a null terminated read group id // r points to a null terminated sample name if ( !strcmp("*",q) || !strcmp("?",q) ) error("Error: the read group IDs \"*\" and \"?\" have a special meaning in the mpileup code. Please fix the code or the bam: %s\n", fname); int accept_rg = 1; if ( bsmpl->sample_list ) { // restrict samples based on the -s/-S options char *name = khash_str2str_get(bsmpl->sample_list,r); if ( bsmpl->sample_logic==0 ) accept_rg = name ? 0 : 1; else if ( !name ) accept_rg = 0; else r = name; } if ( accept_rg && bsmpl->rg_list ) { // restrict readgroups based on the -G option, possibly renaming the sample accept_rg = bsmpl_keep_readgroup(bsmpl,file,q,&r); } if ( accept_rg ) bsmpl_add_readgroup(bsmpl,file,q,r); else { bsmpl_add_readgroup(bsmpl,file,q,NULL); // ignore this RG but note that it was seen in the header nskipped++; } if ( first_smpl<0 ) khash_str2int_get(bsmpl->name2idx,r,&first_smpl); if ( !khash_str2int_has_key(bam_smpls,r) ) khash_str2int_inc(bam_smpls,strdup(r)); *u = ioq; *v = ior; } else break; p = eol; } int nsmpls = khash_str2int_size(bam_smpls); khash_str2int_destroy_free(bam_smpls); const char *smpl_name = NULL; int accept_null_rg = 1; if ( bsmpl->rg_list && !bsmpl_keep_readgroup(bsmpl,file,"?",&smpl_name) ) accept_null_rg = 0; if ( bsmpl->sample_list && first_smpl==-1 ) accept_null_rg = 0; if ( !accept_null_rg && first_smpl==-1 ) { // no suitable read group is available in this bam: ignore the whole file. free(file->fname); if ( file->rg2idx ) khash_str2int_destroy_free(file->rg2idx); bsmpl->nfiles--; return -1; } if ( !accept_null_rg ) return bsmpl->nfiles-1; if ( nsmpls==1 && !nskipped ) { file->default_idx = first_smpl; return bsmpl->nfiles-1; } if ( !smpl_name ) smpl_name = first_smpl==-1 ? file->fname : bsmpl->smpl[first_smpl]; bsmpl_add_readgroup(bsmpl,file,"?",smpl_name); return bsmpl->nfiles-1; }