[med-svn] [plink1.9] 01/02: Imported Upstream version 1.90~b3w-150903

Dylan Aïssi bob.dybian-guest at moszumanska.debian.org
Sat Sep 19 13:32:55 UTC 2015


This is an automated email from the git hooks/post-receive script.

bob.dybian-guest pushed a commit to branch master
in repository plink1.9.

commit 14aef7832180791c6bf5975322da6f6f6c0fd3d5
Author: Dylan Aïssi <bob.dybian at gmail.com>
Date:   Sat Sep 19 15:32:21 2015 +0200

    Imported Upstream version 1.90~b3w-150903
---
 pigz.c          |   56 +-
 plink.c         | 1914 ++++++++++++++++++++++++++++---------------------------
 plink_assoc.c   |   62 +-
 plink_assoc.h   |    2 +
 plink_calc.c    |   82 +--
 plink_cluster.c |   50 +-
 plink_cnv.c     |   77 +--
 plink_common.c  |   65 +-
 plink_common.h  |   18 +-
 plink_data.c    |  635 ++++++++++--------
 plink_dosage.c  |   89 +--
 plink_dosage.h  |    2 +-
 plink_family.c  |  267 +++++---
 plink_filter.c  |   62 +-
 plink_glm.c     |   96 +--
 plink_help.c    |   13 +-
 plink_homozyg.c |   25 +-
 plink_lasso.c   |   22 +-
 plink_ld.c      |  170 +++--
 plink_misc.c    |  169 +++--
 plink_rserve.c  |   12 +-
 plink_set.c     |   81 ++-
 plink_stats.c   |   25 +-
 23 files changed, 2189 insertions(+), 1805 deletions(-)

diff --git a/pigz.c b/pigz.c
index eb15d5e..46b0525 100644
--- a/pigz.c
+++ b/pigz.c
@@ -312,7 +312,9 @@ void parallel_compress(char* out_fname, unsigned char* overflow_buf, uint32_t do
   unsigned char* write_ptr;
   uint32_t last_size;
   if (!gz_outfile) {
-    printf("\nError: Failed to open %s.\n", out_fname);
+    putchar('\n');
+    fflush(stdout);
+    fprintf(stderr, "Error: Failed to open %s.\n", out_fname);
     exit(2);
   }
   do {
@@ -325,7 +327,9 @@ void parallel_compress(char* out_fname, unsigned char* overflow_buf, uint32_t do
     }
     if (last_size) {
       if (!gzwrite(gz_outfile, overflow_buf, last_size)) {
-	fputs("\nError: File write failure.\n", stdout);
+	putchar('\n');
+	fflush(stdout);
+	fputs("Error: File write failure.\n", stderr);
 	gzclose(gz_outfile);
 	exit(6);
       }
@@ -334,7 +338,9 @@ void parallel_compress(char* out_fname, unsigned char* overflow_buf, uint32_t do
       write_ptr = &(overflow_buf[PIGZ_BLOCK_SIZE]);
       while (overflow_ct > PIGZ_BLOCK_SIZE) {
 	if (!gzwrite(gz_outfile, write_ptr, PIGZ_BLOCK_SIZE)) {
-	  fputs("\nError: File write failure.\n", stdout);
+	  putchar('\n');
+	  fflush(stdout);
+	  fputs("Error: File write failure.\n", stderr);
 	  gzclose(gz_outfile);
 	  exit(6);
 	}
@@ -345,7 +351,9 @@ void parallel_compress(char* out_fname, unsigned char* overflow_buf, uint32_t do
     }
   } while (last_size);
   if (gzclose(gz_outfile) != Z_OK) {
-    fputs("\nError: File write failure.\n", stdout);
+    putchar('\n');
+    fflush(stdout);
+    fputs("Error: File write failure.\n", stderr);
     exit(6);
   }
 }
@@ -354,7 +362,9 @@ int32_t pzwrite_init(char* out_fname, unsigned char* overflow_buf, uint32_t do_a
     ps_ptr->outfile = fopen(out_fname, do_append? "ab" : "wb");
     ps_ptr->gz_outfile = NULL;
     if (!ps_ptr->outfile) {
-        printf("\nError: Failed to open %s.\n", out_fname);
+        putchar('\n');
+	fflush(stdout);
+        fprintf(stderr, "Error: Failed to open %s.\n", out_fname);
         return 2; // RET_OPEN_FAIL
     }
     ps_ptr->overflow_buf = overflow_buf;
@@ -365,7 +375,9 @@ void compressed_pzwrite_init(char* out_fname, unsigned char* overflow_buf, uint3
     ps_ptr->outfile = NULL;
     ps_ptr->gz_outfile = gzopen(out_fname, do_append? "ab" : "wb");
     if (!ps_ptr->gz_outfile) {
-        printf("\nError: Failed to open %s.\n", out_fname);
+        putchar('\n');
+        fflush(stdout);
+        fprintf(stderr, "Error: Failed to open %s.\n", out_fname);
         exit(2);
     }
     ps_ptr->overflow_buf = overflow_buf;
@@ -395,7 +407,9 @@ void force_compressed_pzwrite(Pigz_state* ps_ptr, char** writep_ptr, uint32_t wr
     unsigned char* writep = (unsigned char*)(*writep_ptr);
     if (ps_ptr->overflow_buf != writep) {
         if (!gzwrite(ps_ptr->gz_outfile, ps_ptr->overflow_buf, writep - ps_ptr->overflow_buf)) {
-	    fputs("\nError: File write failure.\n", stdout);
+	    putchar('\n');
+	    fflush(stdout);
+	    fputs("Error: File write failure.\n", stderr);
             gzclose(ps_ptr->gz_outfile);
             exit(6);
         }
@@ -415,7 +429,9 @@ int32_t flex_pzputs_std(Pigz_state* ps_ptr, char** writep_ptr, char* ss, uint32_
 	    }
 	} else {
 	    if (!gzwrite(ps_ptr->gz_outfile, ps_ptr->overflow_buf, 2 * PIGZ_BLOCK_SIZE)) {
-	        fputs("\nError: File write failure.\n", stdout);
+	        putchar('\n');
+		fflush(stdout);
+	        fputs("Error: File write failure.\n", stderr);
 	        gzclose(ps_ptr->gz_outfile);
 	        exit(6);
 	    }
@@ -442,7 +458,9 @@ void compressed_pzwrite_close_null(Pigz_state* ps_ptr, char* writep) {
     force_compressed_pzwrite(ps_ptr, &writep, 0);
     ps_ptr->overflow_buf = NULL;
     if (gzclose(ps_ptr->gz_outfile) != Z_OK) {
-        fputs("\nError: File write failure.\n", stdout);
+        putchar('\n');
+	fflush(stdout);
+        fputs("Error: File write failure.\n", stderr);
         exit(6);
     }
 }
@@ -1415,7 +1433,9 @@ int32_t pzwrite_init(char* out_fname, unsigned char* overflow_buf, uint32_t do_a
     // unbuffered, and doesn't need to support Windows
     ps_ptr->outd = open(out_fname, O_WRONLY | (do_append? O_APPEND : (O_CREAT | O_TRUNC)), 0644);
     if (ps_ptr->outd == -1) {
-        printf("\nError: Failed to open %s.\n", out_fname);
+        putchar('\n');
+	fflush(stdout);
+        fprintf(stderr, "Error: Failed to open %s.\n", out_fname);
         return 2; // RET_OPEN_FAIL
     }
     ps_ptr->overflow_buf = overflow_buf;
@@ -1655,7 +1675,9 @@ int32_t write_uncompressed(char* out_fname, unsigned char* overflow_buf, uint32_
   unsigned char* write_ptr;
   uint32_t last_size;
   if (!outfile) {
-    printf("\nError: Failed to open %s.\n", out_fname);
+    putchar('\n');
+    fflush(stdout);
+    fprintf(stderr, "Error: Failed to open %s.\n", out_fname);
     return 2; // RET_OPEN_FAIL
   }
   do {
@@ -1668,7 +1690,9 @@ int32_t write_uncompressed(char* out_fname, unsigned char* overflow_buf, uint32_
     }
     if (last_size) {
       if (!fwrite(overflow_buf, last_size, 1, outfile)) {
-	fputs("\nError: File write failure.\n", stdout);
+	putchar('\n');
+	fflush(stdout);
+	fputs("Error: File write failure.\n", stderr);
 	fclose(outfile);
 	return 6; // RET_WRITE_FAIL
       }
@@ -1677,7 +1701,9 @@ int32_t write_uncompressed(char* out_fname, unsigned char* overflow_buf, uint32_
       write_ptr = &(overflow_buf[PIGZ_BLOCK_SIZE]);
       while (overflow_ct > PIGZ_BLOCK_SIZE) {
 	if (!fwrite(write_ptr, PIGZ_BLOCK_SIZE, 1, outfile)) {
-	  fputs("\nError: File write failure.\n", stdout);
+	  putchar('\n');
+	  fflush(stdout);
+	  fputs("Error: File write failure.\n", stderr);
 	  fclose(outfile);
 	  return 6;
 	}
@@ -1688,7 +1714,9 @@ int32_t write_uncompressed(char* out_fname, unsigned char* overflow_buf, uint32_
     }
   } while (last_size);
   if (fclose(outfile)) {
-    fputs("\nError: File write failure.\n", stdout);
+    putchar('\n');
+    fflush(stdout);
+    fputs("Error: File write failure.\n", stderr);
     return 6;
   }
   return 0;
diff --git a/plink.c b/plink.c
index 7e84ba2..2a006fa 100644
--- a/plink.c
+++ b/plink.c
@@ -91,7 +91,7 @@
 
 const char ver_str[] =
 #ifdef STABLE_BUILD
-  "PLINK v1.90b3u"
+  "PLINK v1.90b3w"
 #else
   "PLINK v1.90p"
 #endif
@@ -103,7 +103,7 @@ const char ver_str[] =
 #else
   " 32-bit"
 #endif
-  " (3 Jul 2015)";
+  " (3 Sep 2015)";
 const char ver_str2[] =
   // include leading space if day < 10, so character length stays the same
   " "
@@ -126,9 +126,9 @@ const char notestr_null_calc2[] = "Commands include --make-bed, --recode, --flip
   #endif
 #else
   #ifndef NOLAPACK
-const char notestr_null_calc2[] = "Commands include --make-bed, --recode, --flip-scan, --merge-list,\n--write-snplist, --list-duplicate-vars, --freqx, --missing, --test-mishap,\n--hardy, --mendel, --ibc, --impute-sex, --indep-pairphase, --r2, --show-tags,\n--blocks, --distance, --genome, --homozyg, --make-rel, --make-grm-gz,\n--rel-cutoff, --cluster, --pca, --neighbour, --ibs-test, --regress-distance,\n--model, --bd, --gxe, --logistic, --dosage, --lasso, --test-missing,\n--make-perm-phen [...]
+const char notestr_null_calc2[] = "Commands include --make-bed, --recode, --flip-scan, --merge-list,\n--write-snplist, --list-duplicate-vars, --freqx, --missing, --test-mishap,\n--hardy, --mendel, --ibc, --impute-sex, --indep-pairphase, --r2, --show-tags,\n--blocks, --distance, --genome, --homozyg, --make-rel, --make-grm-gz,\n--rel-cutoff, --cluster, --pca, --neighbour, --ibs-test, --regress-distance,\n--model, --bd, --gxe, --logistic, --dosage, --lasso, --test-missing,\n--make-perm-phen [...]
   #else
-const char notestr_null_calc2[] = "Commands include --make-bed, --recode, --flip-scan, --merge-list,\n--write-snplist, --list-duplicate-vars, --freqx, --missing, --test-mishap,\n--hardy, --mendel, --ibc, --impute-sex, --indep-pairphase, --r2, --show-tags,\n--blocks, --distance, --genome, --homozyg, --make-rel, --make-grm-gz,\n--rel-cutoff, --cluster, --neighbour, --ibs-test, --regress-distance, --model,\n--bd, --gxe, --logistic, --dosage, --lasso, --test-missing, --make-perm-pheno,\n--td [...]
+const char notestr_null_calc2[] = "Commands include --make-bed, --recode, --flip-scan, --merge-list,\n--write-snplist, --list-duplicate-vars, --freqx, --missing, --test-mishap,\n--hardy, --mendel, --ibc, --impute-sex, --indep-pairphase, --r2, --show-tags,\n--blocks, --distance, --genome, --homozyg, --make-rel, --make-grm-gz,\n--rel-cutoff, --cluster, --neighbour, --ibs-test, --regress-distance, --model,\n--bd, --gxe, --logistic, --dosage, --lasso, --test-missing, --make-perm-pheno,\n--td [...]
   #endif
 #endif
 
@@ -137,16 +137,20 @@ unsigned char* wkspace;
 void disp_exit_msg(int32_t retval) {
   switch (retval) {
   case RET_NOMEM:
-    logprint("\nError: Out of memory.  Try the --memory and/or --parallel flags.\n");
+    logprint("\n");
+    logerrprint("Error: Out of memory.  The --memory flag may be helpful.\n");
     break;
   case RET_WRITE_FAIL:
-    logprint("\nError: File write failure.\n");
+    logprint("\n");
+    logerrprint("Error: File write failure.\n");
     break;
   case RET_READ_FAIL:
-    logprint("\nError: File read failure.\n");
+    logprint("\n");
+    logerrprint("Error: File read failure.\n");
     break;
   case RET_THREAD_CREATE_FAIL:
-    logprint("\nError: Failed to create thread.\n");
+    logprint("\n");
+    logerrprint("Error: Failed to create thread.\n");
     break;
   }
 }
@@ -284,7 +288,7 @@ static inline int32_t relationship_or_ibc_req(uint64_t calculation_type) {
   return (relationship_req(calculation_type) || (calculation_type & CALC_IBC));
 }
 
-int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, char* famname, char* cm_map_fname, char* cm_map_chrname, char* phenoname, char* extractname, char* excludename, char* keepname, char* removename, char* keepfamname, char* removefamname, char* filtername, char* freqname, char* distance_wts_fname, char* read_dists_fname, char* read_dists_id_fname, char* evecname, char* mergename1, char* mergename2, char* mergename3, char* missing_mid_template, char* missing_marke [...]
+int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, char* famname, char* cm_map_fname, char* cm_map_chrname, char* phenoname, char* extractname, char* excludename, char* keepname, char* removename, char* keepfamname, char* removefamname, char* filtername, char* freqname, char* distance_wts_fname, char* read_dists_fname, char* read_dists_id_fname, char* evecname, char* mergename1, char* mergename2, char* mergename3, char* missing_mid_template, char* missing_marke [...]
   FILE* bedfile = NULL;
   FILE* phenofile = NULL;
   uintptr_t unfiltered_marker_ct = 0;
@@ -414,10 +418,10 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
   uintptr_t marker_uidx;
 
   if ((cm_map_fname || update_cm) && (!marker_cms_needed)) {
-    LOGPRINTF("Error: --%s results would never be used.  (Did you forget --make-bed?)\n", cm_map_fname? "cm-map" : "update-cm");
+    LOGERRPRINTF("Error: --%s results would never be used.  (Did you forget --make-bed?)\n", cm_map_fname? "cm-map" : "update-cm");
     goto plink_ret_INVALID_CMDLINE;
   } else if (update_map && (!marker_pos_needed)) {
-    logprint("Error: --update-map results would never be used.  (Did you forget --make-bed?)\n");
+    logerrprint("Error: --update-map results would never be used.  (Did you forget --make-bed?)\n");
     goto plink_ret_INVALID_CMDLINE;
   }
   if (ci_size != 0.0) {
@@ -438,9 +442,9 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     {
       uii = strlen(bedname);
       if ((uii > 8) && ((!memcmp(&(bedname[uii - 8]), ".bed.bed", 8)) || (!memcmp(&(bedname[uii - 8]), ".bim.bed", 8)) || (!memcmp(&(bedname[uii - 8]), ".fam.bed", 8)))) {
-	LOGPRINTFWW("Error: Failed to open %s. (--bfile expects a filename *prefix*; '.bed', '.bim', and '.fam' are automatically appended.)\n", bedname);
+	LOGERRPRINTFWW("Error: Failed to open %s. (--bfile expects a filename *prefix*; '.bed', '.bim', and '.fam' are automatically appended.)\n", bedname);
       } else {
-        LOGPRINTFWW(errstr_fopen, bedname);
+        LOGERRPRINTFWW(errstr_fopen, bedname);
       }
       goto plink_ret_OPEN_FAIL;
     }
@@ -460,21 +464,21 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       memcpy(tbuf, bedname, uii + 1);
       memcpy(&(bedname[uii]), "~", 2);
       if (rename(tbuf, bedname)) {
-	logprint("Error: Failed to append '~' to input .bed filename.\n");
+	logerrprint("Error: Failed to append '~' to input .bed filename.\n");
 	goto plink_ret_OPEN_FAIL;
       }
       uii = strlen(bimname);
       memcpy(tbuf, bimname, uii + 1);
       memcpy(&(bimname[uii]), "~", 2);
       if (rename(tbuf, bimname)) {
-	logprint("Error: Failed to append '~' to input .bim filename.\n");
+	logerrprint("Error: Failed to append '~' to input .bim filename.\n");
 	goto plink_ret_OPEN_FAIL;
       }
       uii = strlen(famname);
       memcpy(tbuf, famname, uii + 1);
       memcpy(&(famname[uii]), "~", 2);
       if (rename(tbuf, famname)) {
-	logprint("Error: Failed to append '~' to input .fam filename.\n");
+	logerrprint("Error: Failed to append '~' to input .fam filename.\n");
 	goto plink_ret_OPEN_FAIL;
       }
     }
@@ -482,7 +486,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_MERGE) {
     if (((fam_cols & FAM_COL_13456) != FAM_COL_13456) || (misc_flags & MISC_AFFECTION_01) || (missing_pheno != -9)) {
-      logprint("Error: --merge/--bmerge/--merge-list cannot be used with an irregularly\nformatted reference fileset (--no-fid, --no-parents, --no-sex, --no-pheno,\n--1, --missing-pheno).  Use --make-bed first.\n");
+      logerrprint("Error: --merge/--bmerge/--merge-list cannot be used with an irregularly\nformatted reference fileset (--no-fid, --no-parents, --no-sex, --no-pheno,\n--1, --missing-pheno).  Use --make-bed first.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     // Only append -merge to the filename stem if --make-bed or --recode lgen
@@ -513,9 +517,9 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     if (!bedfile) {
       uii = strlen(bedname);
       if ((uii > 8) && ((!memcmp(&(bedname[uii - 8]), ".bed.bed", 8)) || (!memcmp(&(bedname[uii - 8]), ".bim.bed", 8)) || (!memcmp(&(bedname[uii - 8]), ".fam.bed", 8)))) {
-	LOGPRINTFWW("Error: Failed to open %s. (--bfile expects a filename *prefix*; '.bed', '.bim', and '.fam' are automatically appended.)\n", bedname);
+	LOGERRPRINTFWW("Error: Failed to open %s. (--bfile expects a filename *prefix*; '.bed', '.bim', and '.fam' are automatically appended.)\n", bedname);
       } else {
-	LOGPRINTFWW(errstr_fopen, bedname);
+	LOGERRPRINTFWW(errstr_fopen, bedname);
       }
       goto plink_ret_OPEN_FAIL;
     }
@@ -532,7 +536,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 	// only warn on long new marker ID, since if there's a long old marker ID
 	// and no long new one, it's reasonable to infer that the user is fixing
 	// the problem, so we shouldn't spam them.
-	logprint("Warning: Unusually long new variant ID(s) in --update-name file.  Double-check\nyour file and command-line parameters, and consider changing your naming\nscheme if you encounter memory problems.\n");
+	logerrprint("Warning: Unusually long new variant ID(s) in --update-name file.  Double-check\nyour file and command-line parameters, and consider changing your naming\nscheme if you encounter memory problems.\n");
       }
       if (ulii > max_marker_id_len) {
 	max_marker_id_len = ulii;
@@ -636,7 +640,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 	retval = load_pheno(phenofile, unfiltered_sample_ct, 0, cptr, max_sample_id_len, uiptr, missing_pheno, (misc_flags / MISC_AFFECTION_01) & 1, mpheno_col, phenoname_str, pheno_nm, &pheno_c, &pheno_d, NULL, 0);
 	if (retval) {
 	  if (retval == LOAD_PHENO_LAST_COL) {
-	    logprintb();
+	    logerrprintb();
 	    retval = RET_INVALID_FORMAT;
 	    wkspace_reset(wkspace_mark);
 	  }
@@ -660,43 +664,43 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       */
       if (calculation_type & (CALC_REGRESS_REL | CALC_REGRESS_DISTANCE | CALC_UNRELATED_HERITABILITY | CALC_GXE)) {
 	if (calculation_type & CALC_REGRESS_REL) {
-	  logprint("Error: --regress-rel calculation requires a scalar phenotype.\n");
+	  logerrprint("Error: --regress-rel calculation requires a scalar phenotype.\n");
 	} else if (calculation_type & CALC_REGRESS_DISTANCE) {
-	  logprint("Error: --regress-distance calculation requires a scalar phenotype.\n");
+	  logerrprint("Error: --regress-distance calculation requires a scalar phenotype.\n");
 	} else if (calculation_type & CALC_UNRELATED_HERITABILITY) {
-	  logprint("Error: --unrelated-heritability requires a scalar phenotype.\n");
+	  logerrprint("Error: --unrelated-heritability requires a scalar phenotype.\n");
 	} else if (calculation_type & CALC_GXE) {
-	  logprint("Error: --gxe requires a scalar phenotype.\n");
+	  logerrprint("Error: --gxe requires a scalar phenotype.\n");
 	}
 	goto plink_ret_INVALID_CMDLINE;
       }
     } else {
       if (calculation_type & CALC_CLUSTER) {
 	if (cluster_ptr->modifier & CLUSTER_CC) {
-	  logprint("Error: --cc requires a case/control phenotype.\n");
+	  logerrprint("Error: --cc requires a case/control phenotype.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	} else if ((cluster_ptr->max_cases != 0xffffffffU) || (cluster_ptr->max_ctrls != 0xffffffffU)) {
-	  logprint("Error: --mcc requires a case/control phenotype.\n");
+	  logerrprint("Error: --mcc requires a case/control phenotype.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	}
       } else if ((calculation_type & CALC_EPI) && (epi_ip->modifier & EPI_FAST)) {
-	logprint("Error: --fast-epistasis requires a case/control phenotype.\n");
+	logerrprint("Error: --fast-epistasis requires a case/control phenotype.\n");
 	goto plink_ret_INVALID_CMDLINE;
       } else if (calculation_type & (CALC_IBS_TEST | CALC_GROUPDIST | CALC_FLIPSCAN)) {
 	if (calculation_type & (CALC_IBS_TEST | CALC_GROUPDIST)) {
-	  logprint("Error: --ibs-test and --groupdist calculations require a case/control\nphenotype.\n");
+	  logerrprint("Error: --ibs-test and --groupdist calculations require a case/control\nphenotype.\n");
 	} else if (calculation_type & CALC_FLIPSCAN) {
-	  logprint("Error: --flip-scan requires a case/control phenotype.\n");
+	  logerrprint("Error: --flip-scan requires a case/control phenotype.\n");
 	}
 	goto plink_ret_INVALID_CMDLINE;
       } else if ((calculation_type & CALC_RECODE) && (recode_modifier & (RECODE_HV | RECODE_HV_1CHR))) {
-	logprint("Error: --recode HV{-1chr} requires a case/control phenotype.\n");
+	logerrprint("Error: --recode HV{-1chr} requires a case/control phenotype.\n");
 	goto plink_ret_INVALID_CMDLINE;
       } else if ((calculation_type & CALC_FST) && (misc_flags & MISC_FST_CC)) {
-	logprint("Error: '--fst case-control' requires a case/control phenotype.\n");
+	logerrprint("Error: '--fst case-control' requires a case/control phenotype.\n");
 	goto plink_ret_INVALID_CMDLINE;
       } else if ((calculation_type & CALC_FREQ) && (misc_flags & MISC_FREQ_CC)) {
-	logprint("Error: '--freq case-control' requires a case/control phenotype.\n");
+	logerrprint("Error: '--freq case-control' requires a case/control phenotype.\n");
 	goto plink_ret_INVALID_CMDLINE;
       }
     }
@@ -704,30 +708,30 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     if (!pheno_all) {
       if (loop_assoc_fname || (!pheno_d)) {
 	if ((calculation_type & CALC_GLM) && (!(glm_modifier & GLM_LOGISTIC))) {
-	  logprint("Error: --linear without --all-pheno requires a scalar phenotype.\n");
+	  logerrprint("Error: --linear without --all-pheno requires a scalar phenotype.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	} else if (calculation_type & CALC_QFAM) {
-	  logprint("Error: QFAM test requires a scalar phenotype.\n");
+	  logerrprint("Error: QFAM test requires a scalar phenotype.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	}
       } else if (!pheno_c) {
 	if ((calculation_type & CALC_MODEL) && (!(model_modifier & MODEL_ASSOC))) {
-	  logprint("Error: --model requires a case/control phenotype.\n");
+	  logerrprint("Error: --model requires a case/control phenotype.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	} else if ((calculation_type & CALC_GLM) && (glm_modifier & GLM_LOGISTIC)) {
-	  logprint("Error: --logistic without --all-pheno requires a case/control phenotype.\n");
+	  logerrprint("Error: --logistic without --all-pheno requires a case/control phenotype.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	} else if (calculation_type & (CALC_CMH | CALC_HOMOG | CALC_TESTMISS | CALC_TDT | CALC_DFAM)) {
 	  if (calculation_type & CALC_CMH) {
-	    logprint("Error: --mh and --mh2 require a case/control phenotype.\n");
+	    logerrprint("Error: --mh and --mh2 require a case/control phenotype.\n");
 	  } else if (calculation_type & CALC_HOMOG) {
-	    logprint("Error: --homog requires a case/control phenotype.\n");
+	    logerrprint("Error: --homog requires a case/control phenotype.\n");
 	  } else if (calculation_type & CALC_TESTMISS) {
-	    logprint("Error: --test-missing requires a case/control phenotype.\n");
+	    logerrprint("Error: --test-missing requires a case/control phenotype.\n");
 	  } else if (calculation_type & CALC_TDT) {
-	    logprint("Error: --tdt requires a case/control phenotype.\n");
+	    logerrprint("Error: --tdt requires a case/control phenotype.\n");
 	  } else {
-	    logprint("Error: --dfam requires a case/control phenotype.\n");
+	    logerrprint("Error: --dfam requires a case/control phenotype.\n");
 	  }
 	  goto plink_ret_INVALID_CMDLINE;
 	}
@@ -738,7 +742,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
   if (cm_map_fname) {
     // need sorted bps, but not marker IDs
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: --cm-map requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
+      logerrprint("Error: --cm-map requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
       goto plink_ret_INVALID_FORMAT;
     }
     retval = apply_cm_map(cm_map_fname, cm_map_chrname, unfiltered_marker_ct, marker_exclude, marker_pos, marker_cms, chrom_info_ptr);
@@ -795,7 +799,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 	}
       } else {
 	if (map_is_unsorted & UNSORTED_BP) {
-	  logprint("Error: '--extract range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
+	  logerrprint("Error: '--extract range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	}
         retval = extract_exclude_range(extractname, marker_pos, unfiltered_marker_ct, marker_exclude, &marker_exclude_ct, 0, chrom_info_ptr);
@@ -814,7 +818,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 	}
       } else {
 	if (map_is_unsorted & UNSORTED_BP) {
-	  logprint("Error: '--exclude range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
+	  logerrprint("Error: '--exclude range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
 	  goto plink_ret_INVALID_CMDLINE;
 	}
         retval = extract_exclude_range(excludename, marker_pos, unfiltered_marker_ct, marker_exclude, &marker_exclude_ct, 1, chrom_info_ptr);
@@ -861,7 +865,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     }
     llxx = ftello(bedfile);
     if (!llxx) {
-      logprint("Error: Empty .bed file.\n");
+      logerrprint("Error: Empty .bed file.\n");
       goto plink_ret_INVALID_FORMAT;
     }
     rewind(bedfile);
@@ -888,14 +892,14 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       if (llxx != llyy) {
 	// probably not PLINK-format at all, so give this error instead of
 	// "invalid file size"
-	logprint("Error: Invalid header bytes in .bed file.\n");
+	logerrprint("Error: Invalid header bytes in .bed file.\n");
 	goto plink_ret_INVALID_FORMAT;
       }
       bed_offset = 2;
     }
     if (llxx != llyy) {
       if ((*tbuf == '#') || ((uii == 3) && (!memcmp(tbuf, "chr", 3)))) {
-	logprint("Error: Invalid header bytes in PLINK 1 .bed file.  (Is this a UCSC Genome\nBrowser BED file instead?)\n");
+	logerrprint("Error: Invalid header bytes in PLINK 1 .bed file.  (Is this a UCSC Genome\nBrowser BED file instead?)\n");
 	goto plink_ret_INVALID_FORMAT;
       } else {
 	sprintf(logbuf, "Error: Invalid .bed file size (expected %" PRId64 " bytes).\n", llyy);
@@ -1007,7 +1011,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       if (uii) {
 	if ((!sex_missing_pheno) && (calculation_type & (CALC_MAKE_BED | CALC_MAKE_FAM | CALC_RECODE | CALC_WRITE_COVAR))) {
 	  if (calculation_type & (~(CALC_MAKE_BED | CALC_MAKE_FAM | CALC_RECODE | CALC_WRITE_COVAR))) {
-	    logprint("Error: When ambiguous-sex samples with phenotype data are present,\n--make-bed/--make-just-fam/--recode/--write-covar usually cannot be combined\nwith other commands.  Split them across multiple PLINK runs, or use\n--allow-no-sex/--must-have-sex.\n");
+	    logerrprint("Error: When ambiguous-sex samples with phenotype data are present,\n--make-bed/--make-just-fam/--recode/--write-covar usually cannot be combined\nwith other commands.  Split them across multiple PLINK runs, or use\n--allow-no-sex/--must-have-sex.\n");
 	    goto plink_ret_INVALID_CMDLINE;
 	  }
 	} else {
@@ -1017,7 +1021,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 	}
       }
       if (uii || pheno_all || loop_assoc_fname) {
-	logprint("Warning: Ignoring phenotypes of missing-sex samples.  If you don't want those\nphenotypes to be ignored, use the --allow-no-sex flag.\n");
+	logerrprint("Warning: Ignoring phenotypes of missing-sex samples.  If you don't want those\nphenotypes to be ignored, use the --allow-no-sex flag.\n");
       }
     }
     if (filter_flags & FILTER_PRUNE) {
@@ -1025,7 +1029,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       zero_trailing_bits(sample_exclude, unfiltered_sample_ct);
       sample_exclude_ct = popcount_longs(sample_exclude, unfiltered_sample_ctl);
       if (sample_exclude_ct == unfiltered_sample_ct) {
-	LOGPRINTF("Error: All %s removed by --prune.\n", g_species_plural);
+	LOGERRPRINTF("Error: All %s removed by --prune.\n", g_species_plural);
 	goto plink_ret_ALL_SAMPLES_EXCLUDED;
       }
       LOGPRINTF("--prune: %" PRIuPTR " %s remaining.\n", unfiltered_sample_ct - sample_exclude_ct, species_str(unfiltered_sample_ct == sample_exclude_ct + 1));
@@ -1033,7 +1037,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
     if (filter_flags & (FILTER_BINARY_CASES | FILTER_BINARY_CONTROLS)) {
       if (!pheno_c) {
-	logprint("Error: --filter-cases/--filter-controls requires a case/control phenotype.\n");
+	logerrprint("Error: --filter-cases/--filter-controls requires a case/control phenotype.\n");
 	goto plink_ret_INVALID_CMDLINE;
       }
       ii = sample_exclude_ct;
@@ -1042,7 +1046,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       // -> flip on fcc == 1
       filter_samples_bitfields(unfiltered_sample_ct, sample_exclude, &sample_exclude_ct, pheno_c, (filter_flags / FILTER_BINARY_CASES) & 1, pheno_nm);
       if (sample_exclude_ct == unfiltered_sample_ct) {
-	LOGPRINTF("Error: All %s removed due to case/control status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_CASES)? "cases" : "controls");
+	LOGERRPRINTF("Error: All %s removed due to case/control status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_CASES)? "cases" : "controls");
 	goto plink_ret_ALL_SAMPLES_EXCLUDED;
       }
       ii = sample_exclude_ct - ii;
@@ -1052,7 +1056,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       ii = sample_exclude_ct;
       filter_samples_bitfields(unfiltered_sample_ct, sample_exclude, &sample_exclude_ct, sex_male, (filter_flags / FILTER_BINARY_MALES) & 1, sex_nm);
       if (sample_exclude_ct == unfiltered_sample_ct) {
-	LOGPRINTF("Error: All %s removed due to gender filter (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_MALES)? "males" : "females");
+	LOGERRPRINTF("Error: All %s removed due to gender filter (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_MALES)? "males" : "females");
 	goto plink_ret_ALL_SAMPLES_EXCLUDED;
       }
       ii = sample_exclude_ct - ii;
@@ -1062,7 +1066,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       ii = sample_exclude_ct;
       filter_samples_bitfields(unfiltered_sample_ct, sample_exclude, &sample_exclude_ct, founder_info, (filter_flags / FILTER_BINARY_FOUNDERS) & 1, NULL);
       if (sample_exclude_ct == unfiltered_sample_ct) {
-	LOGPRINTF("Error: All %s removed due to founder status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_FOUNDERS)? "founders" : "nonfounders");
+	LOGERRPRINTF("Error: All %s removed due to founder status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_FOUNDERS)? "founders" : "nonfounders");
 	goto plink_ret_ALL_SAMPLES_EXCLUDED;
       }
       ii = sample_exclude_ct - ii;
@@ -1099,7 +1103,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     if (!sample_ct) {
       // defensive; currently shouldn't happen since we're actually checking at
       // every filter
-      LOGPRINTF("Error: No %s pass QC.\n", g_species_plural);
+      LOGERRPRINTF("Error: No %s pass QC.\n", g_species_plural);
       goto plink_ret_ALL_SAMPLES_EXCLUDED;
     }
 
@@ -1115,6 +1119,12 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       goto plink_ret_INVALID_CMDLINE_2;
     }
   }
+#ifndef NOLAPACK
+  // Multithreaded BLAS/LAPACK call?  If yes, and either user requested <=
+  // [0.5 * nprocs] threads or nprocs is unknown, warn that BLAS/LAPACK
+  // multithreading is not under PLINK's control.
+  uii = ((!known_procs) || (known_procs * 2 >= g_thread_ct)) && ((calculation_type & (CALC_LASSO | CALC_PCA | CALC_UNRELATED_HERITABILITY)) || ((calculation_type & CALC_GLM) && pheno_d) || cluster_ptr->mds_dim_ct || ((calculation_type & CALC_LD_PRUNE) && (!(ldip->modifier & (LD_PRUNE_PAIRWISE | LD_PRUNE_PAIRPHASE)))));
+#endif
   if (g_thread_ct > 1) {
     if ((calculation_type & (CALC_RELATIONSHIP | CALC_REL_CUTOFF | CALC_GDISTANCE_MASK | CALC_IBS_TEST | CALC_GROUPDIST | CALC_REGRESS_DISTANCE | CALC_GENOME | CALC_REGRESS_REL | CALC_UNRELATED_HERITABILITY | CALC_LD | CALC_PCA | CALC_MAKE_PERM_PHENO | CALC_QFAM)) || ((calculation_type & CALC_MODEL) && (model_modifier & (MODEL_PERM | MODEL_MPERM))) || ((calculation_type & CALC_GLM) && (glm_modifier & (GLM_PERM | GLM_MPERM))) || ((calculation_type & CALC_TESTMISS) && (testmiss_modifier &  [...]
 #ifndef _WIN32
@@ -1127,11 +1137,24 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 ) {
       LOGPRINTF("Using up to %u threads (change this with --threads).\n", g_thread_ct);
     } else {
-      logprint("Using 1 thread (no multithreaded calculations invoked).\n");
+#ifndef NOLAPACK
+      if (uii) {
+	logprint("Using 1 thread.\n");
+      } else {
+#endif
+	logprint("Using 1 thread (no multithreaded calculations invoked.\n");
+#ifndef NOLAPACK
+      }
+#endif
     }
   } else {
     logprint("Using 1 thread.\n");
   }
+#ifndef NOLAPACK
+  if (uii) {
+    logerrprint("Warning: This run includes BLAS/LAPACK linear algebra operations which\ncurrently disregard the --threads limit.  If this is problematic, you may want\nto recompile against single-threaded BLAS/LAPACK.\n");
+  }
+#endif
 
   if (famname[0]) {
     if ((calculation_type & (CALC_SEXCHECK | CALC_MISSING_REPORT | CALC_GENOME | CALC_HOMOZYG | CALC_SCORE | CALC_MENDEL | CALC_HET)) || cluster_ptr->mds_dim_ct) {
@@ -1170,7 +1193,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     if (covar_fname) {
       // update this as more covariate-referencing commands are added
       if (!(calculation_type & (CALC_MAKE_BED | CALC_MAKE_FAM | CALC_RECODE | CALC_WRITE_COVAR | CALC_GXE | CALC_GLM | CALC_LASSO | CALC_RPLUGIN))) {
-	logprint("Warning: Ignoring --covar since no commands reference the covariates.\n");
+	logerrprint("Warning: Ignoring --covar since no commands reference the covariates.\n");
       } else {
 	// if only --gxe, ignore --covar-name/--covar-number
 	uii = (calculation_type & (CALC_MAKE_BED | CALC_MAKE_FAM | CALC_RECODE | CALC_WRITE_COVAR | CALC_GLM | CALC_LASSO | CALC_RPLUGIN))? 1 : 0;
@@ -1211,7 +1234,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
   if (bimname[0]) {
     if (unfiltered_marker_ct == marker_exclude_ct) {
       // defensive
-      logprint("Error: No variants remaining.\n");
+      logerrprint("Error: No variants remaining.\n");
       goto plink_ret_ALL_MARKERS_EXCLUDED;
     }
     plink_maxsnp = calc_plink_maxsnp(unfiltered_marker_ct, marker_exclude, unfiltered_marker_ct - marker_exclude_ct, marker_ids, max_marker_id_len);
@@ -1282,7 +1305,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       bitfield_or(marker_exclude, geno_excl_bitfield, uljj);
       marker_exclude_ct = popcount_longs(marker_exclude, uljj);
       if (marker_exclude_ct == unfiltered_marker_ct) {
-	logprint("Error: All variants excluded due to missing genotype data (--geno).\n");
+	logerrprint("Error: All variants excluded due to missing genotype data (--geno).\n");
 	goto plink_ret_ALL_MARKERS_EXCLUDED;
       }
       ulii = marker_exclude_ct - ulii;
@@ -1307,7 +1330,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     }
     if (min_bp_space) {
       if (map_is_unsorted & UNSORTED_BP) {
-	logprint("Error: --bp-space requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
+	logerrprint("Error: --bp-space requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
 	goto plink_ret_INVALID_FORMAT;
       }
       enforce_min_bp_space(min_bp_space, unfiltered_marker_ct, marker_exclude, marker_pos, &marker_exclude_ct, chrom_info_ptr);
@@ -1339,7 +1362,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     }
     if (sip->fname) {
       if (map_is_unsorted & UNSORTED_BP) {
-	logprint("Error: --set/--make-set requires a sorted .bim file.  Retry this command after\nusing --make-bed to sort your data.\n");
+	logerrprint("Error: --set/--make-set requires a sorted .bim file.  Retry this command after\nusing --make-bed to sort your data.\n");
 	goto plink_ret_INVALID_FORMAT;
       }
       retval = define_sets(sip, unfiltered_marker_ct, marker_exclude, marker_pos, &marker_exclude_ct, marker_ids, max_marker_id_len, chrom_info_ptr);
@@ -1351,7 +1374,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     marker_ct = unfiltered_marker_ct - marker_exclude_ct;
     if (!marker_ct) {
       // defensive
-      logprint("Error: All variants fail QC.\n");
+      logerrprint("Error: All variants fail QC.\n");
       goto plink_ret_ALL_MARKERS_EXCLUDED;
     }
     if (bedfile) {
@@ -1383,11 +1406,11 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 	goto plink_ret_1;
       }
       if (pca_sample_ct < 2) {
-	logprint("Error: Too few samples specified by --pca-cluster-names/--pca-clusters.\n");
+	logerrprint("Error: Too few samples specified by --pca-cluster-names/--pca-clusters.\n");
 	goto plink_ret_1;
       }
       if (pca_sample_ct == sample_ct) {
-	logprint("Warning: --pca-cluster-names/--pca-clusters has no effect since all samples are\nin the named clusters.\n");
+	logerrprint("Warning: --pca-cluster-names/--pca-clusters has no effect since all samples are\nin the named clusters.\n");
 	pca_sample_exclude = NULL;
       } else {
 	LOGPRINTF("--pca-cluster-names/--pca-clusters: %" PRIuPTR " samples specified.\n", pca_sample_ct);
@@ -1432,7 +1455,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
       retval = calc_pca(bedfile, bed_offset, outname, outname_end, calculation_type, relip, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, marker_reverse, unfiltered_sample_ct, sample_exclude, sample_ct, pca_sample_exclude? pca_sample_exclude : sample_exclude, pca_sample_exclude? pca_sample_ct : sample_ct, sample_ids, max_sample_id_len, set_allele_freqs, chrom_info_ptr, rel_ibc);
     } else if (calculation_type & CALC_UNRELATED_HERITABILITY) {
       if (sample_ct != pheno_nm_ct) {
-	logprint("Error: --unrelated-heritability requires phenotype data for all samples.\n(--prune should help.)\n");
+	logerrprint("Error: --unrelated-heritability requires phenotype data for all samples.\n(--prune should help.)\n");
 	goto plink_ret_INVALID_CMDLINE;
       }
       retval = calc_unrelated_herit(calculation_type, relip, unfiltered_sample_ct, sample_exclude, sample_ct, pheno_d, rel_ibc);
@@ -1498,7 +1521,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_DUPVAR) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: --list-duplicate-vars requires a sorted .bim file.  Retry this command\nafter using --make-bed to sort your data.\n");
+      logerrprint("Error: --list-duplicate-vars requires a sorted .bim file.  Retry this command\nafter using --make-bed to sort your data.\n");
       goto plink_ret_INVALID_FORMAT;
     }
     retval = list_duplicate_vars(outname, outname_end, dupvar_modifier, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, marker_pos, chrom_info_ptr, marker_allele_ptrs);
@@ -1545,7 +1568,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_SHOW_TAGS) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: --show-tags requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
+      logerrprint("Error: --show-tags requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = show_tags(ldip, bedfile, bed_offset, marker_ct, unfiltered_marker_ct, marker_exclude, marker_reverse, marker_ids, max_marker_id_len, plink_maxsnp, marker_pos, chrom_info_ptr, unfiltered_sample_ct, founder_info, sex_male, outname, outname_end, hh_exists);
@@ -1556,7 +1579,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_BLOCKS) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: --blocks requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
+      logerrprint("Error: --blocks requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = haploview_blocks(ldip, bedfile, bed_offset, marker_ct, unfiltered_marker_ct, marker_exclude, marker_ids, max_marker_id_len, marker_pos, chrom_info_ptr, set_allele_freqs, unfiltered_sample_ct, founder_info, pheno_nm, sex_male, outname, outname_end, hh_exists);
@@ -1567,7 +1590,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_HOMOZYG) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: Run-of-homozygosity scanning requires a sorted .bim.  Retry this command\nafter using --make-bed to sort your data.\n");
+      logerrprint("Error: Run-of-homozygosity scanning requires a sorted .bim.  Retry this command\nafter using --make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = calc_homozyg(homozyg_ptr, bedfile, bed_offset, marker_ct, unfiltered_marker_ct, marker_exclude, marker_ids, max_marker_id_len, plink_maxsnp, marker_allele_ptrs, max_marker_allele_len, marker_reverse, chrom_info_ptr, marker_pos, sample_ct, unfiltered_sample_ct, sample_exclude, sample_ids, plink_maxfid, plink_maxiid, max_sample_id_len, outname, outname_end, pheno_nm, pheno_c, pheno_d, output_missing_pheno, sex_male);
@@ -1578,7 +1601,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_LD_PRUNE) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: LD-based pruning requires a sorted .bim.  Retry this command after using\n--make-bed to sort your data.\n");
+      logerrprint("Error: LD-based pruning requires a sorted .bim.  Retry this command after using\n--make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     if (!(ldip->modifier & LD_PRUNE_PAIRPHASE)) {
@@ -1593,7 +1616,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_FLIPSCAN) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: LD-based strand flip scanning requires a sorted .bim.  Retry this\ncommand after using --make-bed to sort your data.\n");
+      logerrprint("Error: LD-based strand flip scanning requires a sorted .bim.  Retry this\ncommand after using --make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = flipscan(ldip, bedfile, bed_offset, marker_ct, unfiltered_marker_ct, marker_exclude, marker_reverse, marker_ids, max_marker_id_len, plink_maxsnp, marker_allele_ptrs, max_marker_allele_len, chrom_info_ptr, set_allele_freqs, marker_pos, unfiltered_sample_ct, pheno_nm, pheno_c, founder_info, sex_male, outname, outname_end, hh_exists);
@@ -1611,7 +1634,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_LD) {
     if ((!(ldip->modifier & (LD_MATRIX_SHAPEMASK & LD_INTER_CHR))) && (map_is_unsorted & UNSORTED_BP)) {
-      logprint("Error: Windowed --r/--r2 runs require a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
+      logerrprint("Error: Windowed --r/--r2 runs require a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = ld_report(threads, ldip, bedfile, bed_offset, marker_ct, unfiltered_marker_ct, marker_exclude, marker_reverse, marker_ids, max_marker_id_len, plink_maxsnp, marker_allele_ptrs, max_marker_allele_len, set_allele_freqs, chrom_info_ptr, marker_pos, unfiltered_sample_ct, founder_info, parallel_idx, parallel_tot, sex_male, outname, outname_end, hh_exists);
@@ -1621,7 +1644,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
   }
   if (calculation_type & CALC_TESTMISHAP) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: --test-mishap requires a sorted .bim.  Retry this command after using\n--make-bed to sort your data.\n");
+      logerrprint("Error: --test-mishap requires a sorted .bim.  Retry this command after using\n--make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = test_mishap(bedfile, bed_offset, outname, outname_end, output_min_p, unfiltered_marker_ct, marker_exclude, marker_reverse, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, marker_allele_ptrs, min_maf, chrom_info_ptr, unfiltered_sample_ct, sample_exclude, sample_ct);
@@ -1702,10 +1725,10 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   /*
   if (calculation_type & CALC_REGRESS_PCS_DISTANCE) {
-    logprint("Error: --regress-pcs-distance has not yet been written.\n");
+    logerrprint("Error: --regress-pcs-distance has not yet been written.\n");
     retval = RET_CALC_NOT_YET_SUPPORTED;
     goto plink_ret_1;
-  } else
+  }
   */
   if (distance_req(calculation_type, read_dists_fname)) {
     retval = calc_distance(threads, parallel_idx, parallel_tot, bedfile, bed_offset, outname, outname_end, read_dists_fname, distance_wts_fname, distance_exp, calculation_type, dist_calc_type, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, set_allele_freqs, unfiltered_sample_ct, sample_exclude, sample_ct, sample_ids, max_sample_id_len, chrom_info_ptr);
@@ -1730,7 +1753,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if (calculation_type & CALC_IBS_TEST) {
     if (cluster_starts) {
-      logprint("Error: --ibs-test does not currently support within-cluster permutation.\nContact the developers if you need this.\n");
+      logerrprint("Error: --ibs-test does not currently support within-cluster permutation.\nContact the developers if you need this.\n");
       retval = RET_CALC_NOT_YET_SUPPORTED;
       goto plink_ret_1;
     }
@@ -1747,7 +1770,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
   }
   if (calculation_type & CALC_REGRESS_DISTANCE) {
     if (sample_ct != pheno_nm_ct) {
-      logprint("Error: --regress-distance requires phenotype data for all samples.  (--prune\nshould help.)\n");
+      logerrprint("Error: --regress-distance requires phenotype data for all samples.  (--prune\nshould help.)\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = regress_distance(threads, calculation_type, pheno_d, unfiltered_sample_ct, sample_exclude, sample_ct, g_thread_ct, regress_iters, regress_d);
@@ -1793,7 +1816,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 
   if ((calculation_type & CALC_EPI) && (epi_ip->modifier & (EPI_FAST | EPI_REG))) {
     if ((map_is_unsorted & UNSORTED_BP) && (epi_ip->modifier & EPI_FAST_CASE_ONLY)) {
-      logprint("Error: --fast-epistasis case-only requires a sorted .bim.  Retry this command\nafter using --make-bed to sort your data.\n");
+      logerrprint("Error: --fast-epistasis case-only requires a sorted .bim.  Retry this command\nafter using --make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = epistasis_report(threads, epi_ip, bedfile, bed_offset, marker_ct, unfiltered_marker_ct, marker_exclude, marker_reverse, marker_ids, max_marker_id_len, marker_pos, plink_maxsnp, chrom_info_ptr, unfiltered_sample_ct, pheno_nm, pheno_nm_ct, pheno_ctrl_ct, pheno_c, pheno_d, parallel_idx, parallel_tot, outname, outname_end, output_min_p, glm_vif_thresh, sip);
@@ -1941,7 +1964,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 #ifndef NOLAPACK
 	    retval = glm_linear_assoc(threads, bedfile, bed_offset, outname, outname_end2, glm_modifier, glm_vif_thresh, glm_xchr_model, glm_mperm_val, parameters_range_list_ptr, tests_range_list_ptr, ci_size, ci_zt, pfilter, output_min_p, mtest_adjust, adjust_lambda, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, marker_pos, marker_allele_ptrs, max_marker_allele_len, marker_reverse, condition_mname, condition_fname, chrom_info_ptr, unfiltered_samp [...]
 #else
-            logprint("Warning: Skipping --logistic on --all-pheno QT since this is a no-LAPACK " PROG_NAME_CAPS"\nbuild.\n");
+            logerrprint("Warning: Skipping --logistic on --all-pheno QT since this is a no-LAPACK " PROG_NAME_CAPS"\nbuild.\n");
 #endif
 	  } else {
 	    retval = glm_logistic_assoc(threads, bedfile, bed_offset, outname, outname_end2, glm_modifier, glm_vif_thresh, glm_xchr_model, glm_mperm_val, parameters_range_list_ptr, tests_range_list_ptr, ci_size, ci_zt, pfilter, output_min_p, mtest_adjust, adjust_lambda, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, marker_pos, marker_allele_ptrs, max_marker_allele_len, marker_reverse, condition_mname, condition_fname, chrom_info_ptr, unfiltered_sa [...]
@@ -1951,7 +1974,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 #ifndef NOLAPACK
 	    retval = glm_linear_nosnp(threads, bedfile, bed_offset, outname, outname_end2, glm_modifier, glm_vif_thresh, glm_xchr_model, glm_mperm_val, parameters_range_list_ptr, tests_range_list_ptr, ci_size, ci_zt, pfilter, output_min_p, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, marker_reverse, condition_mname, condition_fname, chrom_info_ptr, unfiltered_sample_ct, sample_ct, sample_exclude, cluster_ct, cluster_map, cluster_starts, mperm_save, pheno_nm_ct [...]
 #else
-            logprint("Warning: Skipping --logistic on --all-pheno QT since this is a no-LAPACK " PROG_NAME_CAPS"\nbuild.\n");
+            logerrprint("Warning: Skipping --logistic on --all-pheno QT since this is a no-LAPACK " PROG_NAME_CAPS"\nbuild.\n");
 #endif
 
 	  } else {
@@ -2012,13 +2035,13 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
 #ifndef NOLAPACK
       if ((calculation_type & CALC_QFAM) && pheno_d) {
 	if (covar_ct) {
-	  logprint("Warning: The QFAM test ignores covariates.\n");
+	  logerrprint("Warning: The QFAM test ignores covariates.\n");
 	}
 	if (cluster_ct) {
-	  logprint("Warning: Clusters have no effect on the QFAM permutation test.\n");
+	  logerrprint("Warning: Clusters have no effect on the QFAM permutation test.\n");
 	}
 	if (mtest_adjust && (fam_ip->qfam_modifier & QFAM_PERM)) {
-	  logprint("Warning: The QFAM test does not support --adjust.  Use max(T) permutation to\nobtain multiple-testing corrected p-values.\n");
+	  logerrprint("Warning: The QFAM test does not support --adjust.  Use max(T) permutation to\nobtain multiple-testing corrected p-values.\n");
 	}
         retval = qfam(threads, bedfile, bed_offset, outname, outname_end2, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, marker_pos, marker_allele_ptrs, marker_reverse, unfiltered_sample_ct, sample_exclude, sample_ct, apip, pheno_nm, pheno_d, founder_info, sex_nm, sex_male, sample_ids, max_sample_id_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, chrom_info_ptr, hh_exists, perm_batch_size, fam_ip);
         if (retval) {
@@ -2030,7 +2053,7 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
   }
   if (calculation_type & CALC_CLUMP) {
     if (map_is_unsorted & UNSORTED_BP) {
-      logprint("Error: --clump requires a sorted .bim.  Retry this command after using\n--make-bed to sort your data.\n");
+      logerrprint("Error: --clump requires a sorted .bim.  Retry this command after using\n--make-bed to sort your data.\n");
       goto plink_ret_INVALID_CMDLINE;
     }
     retval = clump_reports(bedfile, bed_offset, outname, outname_end, unfiltered_marker_ct, marker_exclude, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, marker_pos, marker_allele_ptrs, marker_reverse, chrom_info_ptr, unfiltered_sample_ct, founder_info, clump_ip, sex_male, hh_exists);
@@ -2046,12 +2069,12 @@ int32_t plink(char* outname, char* outname_end, char* bedname, char* bimname, ch
     retval = RET_OPEN_FAIL;
     break;
   plink_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   plink_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
   plink_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
   plink_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -2256,7 +2279,7 @@ int32_t parse_chrom_ranges(uint32_t param_ct, char range_delim, char** argv, uin
     }
   }
   if (!argct) {
-    LOGPRINTF("Error: --%s requires at least one value.\n%s", cur_flag_str, errstr_append);
+    LOGERRPRINTF("Error: --%s requires at least one value.\n%s", cur_flag_str, errstr_append);
     return RET_INVALID_CMDLINE;
   }
   while (0) {
@@ -2264,13 +2287,13 @@ int32_t parse_chrom_ranges(uint32_t param_ct, char range_delim, char** argv, uin
     retval = RET_NOMEM;
     break;
   parse_chrom_ranges_ret_INVALID_CMDLINE_NONSTD:
-    logprint("Error: Chromosome ranges cannot include nonstandard names.\n");
+    logerrprint("Error: Chromosome ranges cannot include nonstandard names.\n");
     retval = RET_INVALID_CMDLINE;
     break;
   parse_chrom_ranges_ret_INVALID_CMDLINE_WWA:
     wordwrap(logbuf, 0);
-    logprintb();
-    logprint(errstr_append);
+    logerrprintb();
+    logerrprint(errstr_append);
     retval = RET_INVALID_CMDLINE;
     break;
   }
@@ -2297,8 +2320,8 @@ int32_t parse_name_ranges(uint32_t param_ct, char range_delim, char** argv, Rang
     cur_arg_ptr = argv[1];
     while (1) {
       if (parse_next_range(param_ct, range_delim, argv, &cur_param_idx, &cur_arg_ptr, &range_start, &rs_len, &range_end, &re_len)) {
-	LOGPRINTFWW("Error: Invalid %s parameter '%s'.\n", argv[0], argv[cur_param_idx]);
-	logprint(errstr_append);
+	LOGERRPRINTFWW("Error: Invalid %s parameter '%s'.\n", argv[0], argv[cur_param_idx]);
+	logerrprint(errstr_append);
         return RET_INVALID_CMDLINE;
       }
       if (!range_start) {
@@ -2317,7 +2340,7 @@ int32_t parse_name_ranges(uint32_t param_ct, char range_delim, char** argv, Rang
     }
   }
   if (!name_ct) {
-    LOGPRINTF("Error: %s requires at least one value.\n%s", argv[0], errstr_append);
+    LOGERRPRINTF("Error: %s requires at least one value.\n%s", argv[0], errstr_append);
     return RET_INVALID_CMDLINE;
   }
   range_list_ptr->name_max_len = ++name_max_len;
@@ -2344,19 +2367,19 @@ int32_t parse_name_ranges(uint32_t param_ct, char range_delim, char** argv, Rang
 	  dup_check = cur_name_str; // actually a numeric check
 	  do {
 	    if (is_not_digit(*dup_check)) {
-	      LOGPRINTFWW("Error: Invalid %s parameter '%s'.\n", argv[0], cur_name_str);
+	      LOGERRPRINTFWW("Error: Invalid %s parameter '%s'.\n", argv[0], cur_name_str);
 	      return RET_INVALID_CMDLINE;
 	    }
 	  } while (*(++dup_check));
 	  if (scan_posint_defcap(cur_name_str, &cur_val)) {
-	    LOGPRINTFWW("Error: Invalid %s parameter '%s'.\n", argv[0], cur_name_str);
+	    LOGERRPRINTFWW("Error: Invalid %s parameter '%s'.\n", argv[0], cur_name_str);
 	    return RET_INVALID_CMDLINE;
 	  }
 	  if (range_list_ptr->starts_range[cur_param_idx]) {
 	    last_val = cur_val;
 	  } else {
 	    if (cur_val <= last_val) {
-	      LOGPRINTFWW("Error: Invalid %s range '%s-%s'.\n", argv[0], &(range_list_ptr->names[(cur_param_idx - 1) * name_max_len]), cur_name_str);
+	      LOGERRPRINTFWW("Error: Invalid %s range '%s-%s'.\n", argv[0], &(range_list_ptr->names[(cur_param_idx - 1) * name_max_len]), cur_name_str);
 	      return RET_INVALID_CMDLINE;
 	    }
 	    last_val = 0;
@@ -2369,7 +2392,7 @@ int32_t parse_name_ranges(uint32_t param_ct, char range_delim, char** argv, Rang
     dup_check = range_list_ptr->names;
     while (dup_check < cur_name_str) {
       if (!memcmp(dup_check, cur_name_str, rs_len + 1)) {
-	LOGPRINTFWW("Error: Duplicate %s parameter '%s'.\n", argv[0], cur_name_str);
+	LOGERRPRINTFWW("Error: Duplicate %s parameter '%s'.\n", argv[0], cur_name_str);
 	return RET_INVALID_CMDLINE;
       }
       dup_check = &(dup_check[name_max_len]);
@@ -2381,7 +2404,7 @@ int32_t parse_name_ranges(uint32_t param_ct, char range_delim, char** argv, Rang
       dup_check = range_list_ptr->names;
       while (dup_check < cur_name_str) {
 	if (!memcmp(dup_check, cur_name_str, rs_len + 1)) {
-	  LOGPRINTFWW("Error: Duplicate %s parameter '%s'.\n", argv[0], cur_name_str);
+	  LOGERRPRINTFWW("Error: Duplicate %s parameter '%s'.\n", argv[0], cur_name_str);
 	  return RET_INVALID_CMDLINE;
 	}
         dup_check = &(dup_check[name_max_len]);
@@ -2433,7 +2456,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
   tbuf[MAXLINELEN - 1] = ' ';
   if (!fgets(tbuf, MAXLINELEN, rerunfile)) {
     print_ver();
-    fputs("Error: Empty log file for --rerun.\n", stdout);
+    fflush(stdout);
+    fputs("Error: Empty log file for --rerun.\n", stderr);
     goto rerun_ret_INVALID_FORMAT;
   }
   if (!tbuf[MAXLINELEN - 1]) {
@@ -2441,7 +2465,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
   }
   if (!fgets(tbuf, MAXLINELEN, rerunfile)) {
     print_ver();
-    fputs("Error: Only one line in --rerun log file.\n", stdout);
+    fflush(stdout);
+    fputs("Error: Only one line in --rerun log file.\n", stderr);
     goto rerun_ret_INVALID_FORMAT;
   }
   line_idx++;
@@ -2453,7 +2478,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
     fclose_null(&rerunfile);
     if (scan_posint_capped(tbuf, &loaded_arg_ct, (MAXLINELEN / 2) / 10, (MAXLINELEN / 2) % 10)) {
       print_ver();
-      fputs("Error: Invalid argument count on line 2 of --rerun log file.\n", stdout);
+      fflush(stdout);
+      fputs("Error: Invalid argument count on line 2 of --rerun log file.\n", stderr);
       goto rerun_ret_INVALID_FORMAT;
     }
     line_byte_ct = strlen(tbuf) + 1;
@@ -2471,7 +2497,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
       line_idx++;
       if (!fgets(tbuf, MAXLINELEN, rerunfile)) {
 	print_ver();
-	fputs("Error: Invalid log file for --rerun.\n", stdout);
+	fflush(stdout);
+	fputs("Error: Invalid log file for --rerun.\n", stderr);
 	goto rerun_ret_INVALID_FORMAT;
       }
     }
@@ -2504,7 +2531,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
       load_ptr = argptr;
       if (load_ptr >= &(tbuf[MAXLINELEN])) {
 	print_ver();
-	fputs("Error: --rerun argument sequence too long.\n", stdout);
+	fflush(stdout);
+	fputs("Error: --rerun argument sequence too long.\n", stderr);
 	goto rerun_ret_INVALID_FORMAT;
       }
     }
@@ -2527,7 +2555,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
   do {
     if (no_more_tokens_kns(sptr)) {
       print_ver();
-      fputs("Error: Line 2 of --rerun log file has fewer tokens than expected.\n", stdout);
+      fflush(stdout);
+      fputs("Error: Line 2 of --rerun log file has fewer tokens than expected.\n", stderr);
       goto rerun_ret_INVALID_FORMAT;
     }
     argptr = is_flag_start(sptr);
@@ -2605,7 +2634,8 @@ int32_t rerun(uint32_t rerun_argv_pos, uint32_t rerun_parameter_present, int32_t
     break;
   rerun_ret_LONG_LINE:
     print_ver();
-    printf("Error: Line %" PRIuPTR " of --rerun log file is pathologically long.\n", line_idx);
+    fflush(stdout);
+    fprintf(stderr, "Error: Line %" PRIuPTR " of --rerun log file is pathologically long.\n", line_idx);
   rerun_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -2642,7 +2672,7 @@ int32_t alloc_string(char** sbuf, const char* source) {
 int32_t alloc_fname(char** fnbuf, char* source, char* argptr, uint32_t extra_size) {
   uint32_t slen = strlen(source) + 1;
   if (slen > (FNAMESIZE - extra_size)) {
-    LOGPRINTF("Error: --%s filename too long.\n", argptr);
+    LOGERRPRINTF("Error: --%s filename too long.\n", argptr);
     return RET_OPEN_FAIL;
   }
   *fnbuf = (char*)malloc((slen + extra_size) * sizeof(char));
@@ -2721,7 +2751,7 @@ int32_t alloc_2col(Two_col_params** tcbuf, char** params_ptr, char* argptr, uint
   uint32_t slen = strlen(*params_ptr) + 1;
   char cc;
   if (slen > FNAMESIZE) {
-    LOGPRINTF("Error: --%s filename too long.\n", argptr);
+    LOGERRPRINTF("Error: --%s filename too long.\n", argptr);
     return RET_OPEN_FAIL;
   }
   *tcbuf = (Two_col_params*)malloc(sizeof(Two_col_params) + slen);
@@ -2733,12 +2763,12 @@ int32_t alloc_2col(Two_col_params** tcbuf, char** params_ptr, char* argptr, uint
   (*tcbuf)->skipchar = '\0';
   if (param_ct > 1) {
     if (scan_posint_defcap(params_ptr[1], &((*tcbuf)->colx))) {
-      LOGPRINTF("Error: Invalid --%s column number.\n", argptr);
+      LOGERRPRINTF("Error: Invalid --%s column number.\n", argptr);
       return RET_INVALID_FORMAT;
     }
     if (param_ct > 2) {
       if (scan_posint_defcap(params_ptr[2], &((*tcbuf)->colid))) {
-	LOGPRINTF("Error: Invalid --%s variant ID column number.\n", argptr);
+	LOGERRPRINTF("Error: Invalid --%s variant ID column number.\n", argptr);
 	return RET_INVALID_FORMAT;
       }
       if (param_ct == 4) {
@@ -2752,7 +2782,7 @@ int32_t alloc_2col(Two_col_params** tcbuf, char** params_ptr, char* argptr, uint
 	} else {
 	  if (scan_uint_defcap(params_ptr[3], &((*tcbuf)->skip))) {
 	  alloc_2col_invalid_skip:
-	    LOGPRINTF("Error: Invalid --%s skip parameter.  This needs to either be a\nsingle character (usually '#') which, when present at the start of a line,\nindicates it should be skipped; or the number of initial lines to skip.  (Note\nthat in shells such as bash, '#' is a special character that must be\nsurrounded by single- or double-quotes to be parsed correctly.)\n", argptr);
+	    LOGERRPRINTF("Error: Invalid --%s skip parameter.  This needs to either be a\nsingle character (usually '#') which, when present at the start of a line,\nindicates it should be skipped; or the number of initial lines to skip.  (Note\nthat in shells such as bash, '#' is a special character that must be\nsurrounded by single- or double-quotes to be parsed correctly.)\n", argptr);
 	    return RET_INVALID_FORMAT;
 	  }
 	}
@@ -2761,7 +2791,7 @@ int32_t alloc_2col(Two_col_params** tcbuf, char** params_ptr, char* argptr, uint
       (*tcbuf)->colid = 1;
     }
     if ((*tcbuf)->colx == (*tcbuf)->colid) {
-      LOGPRINTF("Error: Column numbers for --%s cannot be equal.\n%s", argptr, errstr_append);
+      LOGERRPRINTF("Error: Column numbers for --%s cannot be equal.\n%s", argptr, errstr_append);
       return RET_INVALID_FORMAT;
     }
   } else {
@@ -2789,7 +2819,7 @@ int32_t flag_match(const char* to_match, uint32_t* cur_flag_ptr, uint32_t flag_c
 
 uint32_t species_flag(uint32_t* species_code_ptr, uint32_t new_code) {
   if (*species_code_ptr) {
-    logprint("Error: Multiple chromosome set flags.\n");
+    logerrprint("Error: Multiple chromosome set flags.\n");
     return 1;
   }
   *species_code_ptr = new_code;
@@ -2800,7 +2830,7 @@ uint32_t valid_varid_template_string(char* varid_str, const char* flag_name) {
   char* sptr = strchr(varid_str, '@');
   char* sptr2 = strchr(varid_str, '#');
   if ((!sptr) || (!sptr2) || strchr(&(sptr[1]), '@') || strchr(&(sptr2[1]), '#')) {
-    LOGPRINTFWW("Error: The %s template string requires exactly one '@' and one '#'.\n", flag_name);
+    LOGERRPRINTFWW("Error: The %s template string requires exactly one '@' and one '#'.\n", flag_name);
     return 0;
   }
   // snp/nonsnp is not sufficient for assigning unique IDs to unnamed 1000
@@ -2817,7 +2847,7 @@ uint32_t valid_varid_template_string(char* varid_str, const char* flag_name) {
   if (sptr) {
     sptr2 = strchr(&(sptr[1]), '$');
     if ((!sptr2) || strchr(&(sptr2[1]), '$') || (!(((sptr[1] == '1') && (sptr2[1] == '2')) || ((sptr[1] == '2') && (sptr2[1] == '1'))))) {
-      LOGPRINTFWW("Error: The %s template string requires either no instances of '$', or exactly one '$1' and one '$2'.\n", flag_name);
+      LOGERRPRINTFWW("Error: The %s template string requires either no instances of '$', or exactly one '$1' and one '$2'.\n", flag_name);
       return 0;
     }
   }
@@ -2886,7 +2916,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
     }
     if (ii < 0) {
       if (param_ct > 1) {
-	logprint("Error: --chr-set does not accept multiple parameters in haploid mode.\n");
+	logerrprint("Error: --chr-set does not accept multiple parameters in haploid mode.\n");
 	goto init_delim_and_species_ret_INVALID_CMDLINE_A;
       }
       ii = -ii;
@@ -2939,7 +2969,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
     if (param_count(argc, argv, flag_map[flag_idx - 1])) {
-      logprint("Error: --cow doesn't accept parameters.\n");
+      logerrprint("Error: --cow doesn't accept parameters.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
   }
@@ -2952,10 +2982,10 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
     }
     *range_delim_ptr = extract_char_param(argv[cur_arg + 1]);
     if (!(*range_delim_ptr)) {
-      logprint("Error: --d parameter too long (must be a single character).\n");
+      logerrprint("Error: --d parameter too long (must be a single character).\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE_A;
     } else if ((*range_delim_ptr == '-') || (*range_delim_ptr == ',')) {
-      logprint("Error: --d parameter cannot be '-' or ','.\n");
+      logerrprint("Error: --d parameter cannot be '-' or ','.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE_A;
     }
   }
@@ -2964,7 +2994,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
     if (param_count(argc, argv, flag_map[flag_idx - 1])) {
-      logprint("Error: --dog doesn't accept parameters.\n");
+      logerrprint("Error: --dog doesn't accept parameters.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
   }
@@ -2973,7 +3003,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
     if (param_count(argc, argv, flag_map[flag_idx - 1])) {
-      logprint("Error: --horse doesn't accept parameters.\n");
+      logerrprint("Error: --horse doesn't accept parameters.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
   }
@@ -2982,7 +3012,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
     if (param_count(argc, argv, flag_map[flag_idx - 1])) {
-      logprint("Error: --mouse doesn't accept parameters.\n");
+      logerrprint("Error: --mouse doesn't accept parameters.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
   }
@@ -2991,7 +3021,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
     if (param_count(argc, argv, flag_map[flag_idx - 1])) {
-      logprint("Error: --rice doesn't accept parameters.\n");
+      logerrprint("Error: --rice doesn't accept parameters.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
   }
@@ -3000,7 +3030,7 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
     if (param_count(argc, argv, flag_map[flag_idx - 1])) {
-      logprint("Error: --sheep doesn't accept parameters.\n");
+      logerrprint("Error: --sheep doesn't accept parameters.\n");
       goto init_delim_and_species_ret_INVALID_CMDLINE;
     }
   }
@@ -3057,9 +3087,9 @@ int32_t init_delim_and_species(uint32_t flag_ct, char* flag_buf, uint32_t* flag_
   init_delim_and_species_ret_INVALID_CMDLINE_WWA:
     wordwrap(logbuf, 0);
   init_delim_and_species_ret_INVALID_CMDLINE_2A:
-    logprintb();
+    logerrprintb();
   init_delim_and_species_ret_INVALID_CMDLINE_A:
-    logprint(errstr_append);
+    logerrprint(errstr_append);
     retval = RET_INVALID_CMDLINE;
     break;
   init_delim_and_species_ret_INVALID_CMDLINE:
@@ -3092,7 +3122,7 @@ void fill_chrom_mask(Chrom_info* chrom_info_ptr) {
 
 int32_t recode_type_set(uint32_t* recode_modifier_ptr, uint32_t cur_code) {
   if (*recode_modifier_ptr & (RECODE_TYPEMASK - cur_code)) {
-    logprint("Error: Conflicting --recode modifiers.\n");
+    logerrprint("Error: Conflicting --recode modifiers.\n");
     return -1;
   }
   *recode_modifier_ptr |= cur_code;
@@ -3370,6 +3400,7 @@ int32_t main(int32_t argc, char** argv) {
   char* flagptr;
   double dxx;
   char cc;
+  uint32_t known_procs;
   uint32_t uii;
   uint32_t ujj;
   uint32_t ukk;
@@ -3416,8 +3447,9 @@ int32_t main(int32_t argc, char** argv) {
       for (ujj = uii + 2; ujj < (uint32_t)argc; ujj++) {
 	if ((!strcmp("-script", argv[ujj])) || (!strcmp("--script", argv[ujj]))) {
 	  print_ver();
-	  fputs("Error: Multiple --script flags.  Merge the files into one.\n", stdout);
-	  fputs(errstr_append, stdout);
+	  fflush(stdout);
+	  fputs("Error: Multiple --script flags.  Merge the files into one.\n", stderr);
+	  fputs(errstr_append, stderr);
 	  goto main_ret_INVALID_CMDLINE;
 	}
       }
@@ -3440,7 +3472,8 @@ int32_t main(int32_t argc, char** argv) {
 	// could actually happen if user enters parameters in the wrong order,
 	// so may as well catch it and print a somewhat informative error msg
 	print_ver();
-        fputs("Error: --script file too large.\n", stdout);
+	fflush(stdout);
+        fputs("Error: --script file too large.\n", stderr);
         goto main_ret_NOMEM;
       }
       rewind(scriptfile);
@@ -3503,7 +3536,8 @@ int32_t main(int32_t argc, char** argv) {
       for (ukk = uii + ujj + 1; ukk < (uint32_t)argc; ukk++) {
 	if ((!strcmp("-rerun", argv[ukk])) || (!strcmp("--rerun", argv[ukk]))) {
 	  print_ver();
-	  fputs("Error: Duplicate --rerun flag.\n", stdout);
+	  fflush(stdout);
+	  fputs("Error: Duplicate --rerun flag.\n", stderr);
 	  goto main_ret_INVALID_CMDLINE;
 	}
       }
@@ -3515,8 +3549,9 @@ int32_t main(int32_t argc, char** argv) {
   }
   if ((cur_arg < (uint32_t)argc) && (!is_flag(argv[cur_arg]))) {
     print_ver();
-    fputs("Error: First parameter must be a flag.\n", stdout);
-    fputs(errstr_append, stdout);
+    fflush(stdout);
+    fputs("Error: First parameter must be a flag.\n", stderr);
+    fputs(errstr_append, stderr);
     goto main_ret_INVALID_CMDLINE;
   }
   flag_ct = 0;
@@ -3592,7 +3627,8 @@ int32_t main(int32_t argc, char** argv) {
       switch (*argptr) {
       case '\0':
 	// special case, since we reserve empty names for preprocessed flags
-	fputs("Error: Unrecognized flag ('--').\n", stdout);
+	fflush(stdout);
+	fputs("Error: Unrecognized flag ('--').\n", stderr);
 	goto main_ret_INVALID_CMDLINE;
       case 'F':
 	if (!strcmp(argptr, "Fst")) {
@@ -3668,11 +3704,13 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_flag_copy;
       case 'h':
         if (!strcmp(argptr, "hwe2")) {
-	  fputs("Warning: --hwe2 flag is obsolete, and now treated as an alias for '--hwe midp'.\n", stdout);
+	  fflush(stdout);
+	  fputs("Warning: --hwe2 flag is obsolete, and now treated as an alias for '--hwe midp'.\n", stderr);
 	  memcpy(flagptr, "hwe midp", 9);
 	  break;
         } else if (!strcmp(argptr, "hardy2")) {
-	  fputs("Warning: --hardy2 flag is obsolete, and now treated as an alias for\n'--hardy midp'.\n", stdout);
+	  fflush(stdout);
+	  fputs("Warning: --hardy2 flag is obsolete, and now treated as an alias for\n'--hardy midp'.\n", stderr);
 	  memcpy(flagptr, "hardy midp", 11);
 	  break;
 	}
@@ -3856,7 +3894,8 @@ int32_t main(int32_t argc, char** argv) {
     ukk = strlen_se(&(flag_buf[cur_flag * MAX_FLAG_LEN]));
     if ((ujj == ukk) && (!memcmp(&(flag_buf[(cur_flag - 1) * MAX_FLAG_LEN]), &(flag_buf[cur_flag * MAX_FLAG_LEN]), ukk))) {
       flag_buf[cur_flag * MAX_FLAG_LEN + ukk] = '\0'; // just in case of aliases
-      printf("Error: Duplicate --%s flag.\n", &(flag_buf[cur_flag * MAX_FLAG_LEN]));
+      fflush(stdout);
+      fprintf(stderr, "Error: Duplicate --%s flag.\n", &(flag_buf[cur_flag * MAX_FLAG_LEN]));
       goto main_ret_INVALID_CMDLINE;
     }
     ujj = ukk;
@@ -3875,7 +3914,8 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_ret_INVALID_CMDLINE;
       }
       if (strlen(argv[ujj + 1]) > (FNAMESIZE - MAX_POST_EXT)) {
-	fputs("Error: --out parameter too long.\n", stdout);
+	fflush(stdout);
+	fputs("Error: --out parameter too long.\n", stderr);
 	goto main_ret_OPEN_FAIL;
       }
       uii = strlen(argv[ujj + 1]);
@@ -3889,11 +3929,12 @@ int32_t main(int32_t argc, char** argv) {
   memcpy(&(outname[uii]), ".log", 5);
   logfile = fopen(outname, "w");
   if (!logfile) {
-    printf("Error: Failed to open %s.  Try ", outname);
+    fflush(stdout);
+    fprintf(stderr, "Error: Failed to open %s.  Try ", outname);
     if (!memcmp(outname, PROG_NAME_STR, 6)) {
-      printf("using --out.\n");
+      fputs("using --out.\n", stderr);
     } else {
-      printf("changing the --out parameter.\n");
+      fputs("changing the --out parameter.\n", stderr);
     }
     goto main_ret_OPEN_FAIL;
   }
@@ -3949,12 +3990,15 @@ int32_t main(int32_t argc, char** argv) {
 #ifdef _WIN32
   GetSystemInfo(&sysinfo);
   g_thread_ct = sysinfo.dwNumberOfProcessors;
+  known_procs = g_thread_ct;
 #else
   ii = sysconf(_SC_NPROCESSORS_ONLN);
   if (ii == -1) {
     g_thread_ct = 1;
+    known_procs = 0;
   } else {
     g_thread_ct = ii;
+    known_procs = ii;
   }
 #endif
   if (g_thread_ct > 8) {
@@ -3997,7 +4041,7 @@ int32_t main(int32_t argc, char** argv) {
     case '2':
       if (!memcmp(argptr2, "3file", 6)) {
 	if (chrom_info.species != SPECIES_HUMAN) {
-	  logprint("Error: --23file cannot be used with a nonhuman species flag.\n");
+	  logerrprint("Error: --23file cannot be used with a nonhuman species flag.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 7)) {
@@ -4005,13 +4049,13 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	ii = strlen(argv[cur_arg + 1]);
 	if (ii > FNAMESIZE - 1) {
-	  logprint("Error: --23file filename too long.\n");
+	  logerrprint("Error: --23file filename too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
         memcpy(pedname, argv[cur_arg + 1], ii + 1);
 	if (param_ct > 1) {
 	  if (strchr(argv[cur_arg + 2], ' ')) {
-	    logprint("Error: Space present in --23file family ID.\n");
+	    logerrprint("Error: Space present in --23file family ID.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  if (alloc_string(&fid_23, argv[cur_arg + 2])) {
@@ -4019,7 +4063,7 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	  if (param_ct > 2) {
 	    if (strchr(argv[cur_arg + 3], ' ')) {
-	      logprint("Error: Space present in --23file sample ID.\n");
+	      logerrprint("Error: Space present in --23file sample ID.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (alloc_string(&iid_23, argv[cur_arg + 3])) {
@@ -4034,7 +4078,7 @@ int32_t main(int32_t argc, char** argv) {
 	      } else if (cc == '0') {
 		modifier_23 |= M23_FORCE_MISSING_SEX;
 	      } else if ((cc != 'I') && (cc != 'i')) {
-		logprint("Error: Invalid --23file sex parameter (M or 1 = male, F or 2 = female,\nI = infer from data, 0 = force missing).\n");
+		logerrprint("Error: Invalid --23file sex parameter (M or 1 = male, F or 2 = female,\nI = infer from data, 0 = force missing).\n");
 		goto main_ret_INVALID_CMDLINE;
 	      }
 	      if (param_ct > 4) {
@@ -4044,7 +4088,7 @@ int32_t main(int32_t argc, char** argv) {
 		}
 		if (param_ct > 5) {
 		  if (strchr(argv[cur_arg + 6], ' ')) {
-		    logprint("Error: Space present in --23file paternal ID.\n");
+		    logerrprint("Error: Space present in --23file paternal ID.\n");
 		    goto main_ret_INVALID_CMDLINE;
 		  }
 		  if (alloc_string(&paternal_id_23, argv[cur_arg + 6])) {
@@ -4052,7 +4096,7 @@ int32_t main(int32_t argc, char** argv) {
 		  }
 		  if (param_ct > 6) {
 		    if (strchr(argv[cur_arg + 7], ' ')) {
-		      logprint("Error: Space present in --23file maternal ID.\n");
+		      logerrprint("Error: Space present in --23file maternal ID.\n");
 		      goto main_ret_INVALID_CMDLINE;
 		    }
 		    if (alloc_string(&maternal_id_23, argv[cur_arg + 7])) {
@@ -4121,16 +4165,16 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_RPLUGIN;
 #else
   #ifdef _WIN32
-        logprint("Error: --R does not currently support Windows.\n");
+        logerrprint("Error: --R does not currently support Windows.\n");
   #else
-	logprint("Error: --R requires " PROG_NAME_CAPS " to be built with a C++, not a pure C, compiler.\n");
+	logerrprint("Error: --R requires " PROG_NAME_CAPS " to be built with a C++, not a pure C, compiler.\n");
   #endif
 	goto main_ret_INVALID_CMDLINE;
 #endif
 
       } else if (!memcmp(argptr2, "-port", 6)) {
 	if (!rplugin_fname) {
-	  logprint("Error: --R-port must be used with --R.\n");
+	  logerrprint("Error: --R-port must be used with --R.\n");
 	  goto main_ret_INVALID_CMDLINE;
         }
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4142,7 +4186,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "-debug", 7)) {
 	if (!rplugin_fname) {
-	  logprint("Error: --R-debug must be used with --R.\n");
+	  logerrprint("Error: --R-debug must be used with --R.\n");
 	  goto main_ret_INVALID_CMDLINE;
         }
 	logprint("Note: --R-debug flag deprecated.  Use e.g. '--R [filename] debug'.\n");
@@ -4170,11 +4214,11 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "utosome-xy", 11)) {
 	if (chrom_flag_present) {
-          logprint("Error: --autosome-xy cannot be used with --autosome.\n");
+          logerrprint("Error: --autosome-xy cannot be used with --autosome.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (chrom_info.xy_code == -1) {
-	  logprint("Error: --autosome-xy used with a species lacking an XY region.\n");
+	  logerrprint("Error: --autosome-xy used with a species lacking an XY region.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	fill_bits(chrom_info.chrom_mask, 1, chrom_info.autosome_ct);
@@ -4183,7 +4227,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "llow-extra-chr", 15)) {
 	if (load_rare == LOAD_RARE_23) {
-	  logprint("Error: --allow-extra-chr cannot currently be used with --23file.\n");
+	  logerrprint("Error: --allow-extra-chr cannot currently be used with --23file.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -4219,7 +4263,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_NODOSAGE | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "lleleACGT", 9)) {
 	if (allelexxxx) {
-	  logprint("Error: --allele1234 and --alleleACGT cannot be used together.\n");
+	  logerrprint("Error: --allele1234 and --alleleACGT cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -4248,31 +4292,31 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "counts")) {
 	    if (model_modifier & MODEL_QMASK) {
-	      logprint("Error: --assoc 'qt-means' modifier does not make sense with 'counts'.\n");
+	      logerrprint("Error: --assoc 'qt-means' modifier does not make sense with 'counts'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_ASSOC_COUNTS;
 	  } else if (!strcmp(argv[cur_arg + uii], "fisher")) {
 	    if (model_modifier & MODEL_QMASK) {
-	      logprint("Error: --assoc 'qt-means'/'lin' does not make sense with 'fisher'.\n");
+	      logerrprint("Error: --assoc 'qt-means'/'lin' does not make sense with 'fisher'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_FISHER;
 	  } else if (!strcmp(argv[cur_arg + uii], "fisher-midp")) {
             if (model_modifier & MODEL_QMASK) {
-              logprint("Error: --assoc 'qt-means'/'lin' does not make sense with 'fisher-midp'.\n");
+              logerrprint("Error: --assoc 'qt-means'/'lin' does not make sense with 'fisher-midp'.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
             model_modifier |= MODEL_FISHER | MODEL_FISHER_MIDP;
 	  } else if (!strcmp(argv[cur_arg + uii], "perm")) {
 	    if (model_modifier & MODEL_MPERM) {
-	      logprint("Error: --assoc 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --assoc 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PERM;
 	  } else if (!strcmp(argv[cur_arg + uii], "genedrop")) {
 	    if (model_modifier & MODEL_QMASK) {
-	      logprint("Error: --assoc 'qt-means'/'lin' does not make sense with 'genedrop'.\n");
+	      logerrprint("Error: --assoc 'qt-means'/'lin' does not make sense with 'genedrop'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_GENEDROP;
@@ -4280,10 +4324,10 @@ int32_t main(int32_t argc, char** argv) {
 	    model_modifier |= MODEL_PERM_COUNT;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
 	    if (model_modifier & MODEL_PERM) {
-	      logprint("Error: --assoc 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --assoc 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (model_modifier & MODEL_MPERM) {
-	      logprint("Error: Duplicate --assoc 'mperm' modifier.\n");
+	      logerrprint("Error: Duplicate --assoc 'mperm' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &model_mperm_val)) {
@@ -4293,18 +4337,18 @@ int32_t main(int32_t argc, char** argv) {
 	    model_modifier |= MODEL_MPERM;
 	  } else if (!strcmp(argv[cur_arg + uii], "qt-means")) {
 	    if (model_modifier & MODEL_DMASK) {
-	      logprint("Error: --assoc 'qt-means' does not make sense with a case/control-specific\nmodifier.\n");
+	      logerrprint("Error: --assoc 'qt-means' does not make sense with a case/control-specific\nmodifier.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_QT_MEANS;
 	  } else if (!strcmp(argv[cur_arg + uii], "lin")) {
 	    if (model_modifier & MODEL_DMASK) {
-	      logprint("Error: --assoc 'lin' does not make sense with a case/control-specific modifier.\n");
+	      logerrprint("Error: --assoc 'lin' does not make sense with a case/control-specific modifier.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_LIN;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-	    logprint("Error: Improper --assoc mperm syntax.  (Use '--assoc mperm=[value]'.)\n");
+	    logerrprint("Error: Improper --assoc mperm syntax.  (Use '--assoc mperm=[value]'.)\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else if (!strcmp(argv[cur_arg + uii], "set-test")) {
 	    model_modifier |= MODEL_SET_TEST;
@@ -4350,7 +4394,7 @@ int32_t main(int32_t argc, char** argv) {
 	// may as well disallow equality since there's no reason not to use
 	// max(T) then...
 	if (aperm.min >= aperm.max) {
-	  logprint("Error: --aperm min permutation count must be smaller than max.\n");
+	  logerrprint("Error: --aperm min permutation count must be smaller than max.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (param_ct > 2) {
@@ -4396,7 +4440,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_NODOSAGE | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "2-allele", 9)) {
 	if (a1alleles) {
-	  logprint("Error: --a2-allele cannot be used with --a1-allele.\n");
+	  logerrprint("Error: --a2-allele cannot be used with --a1-allele.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 4)) {
@@ -4444,13 +4488,13 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	  } else if ((ujj == 2) && (!memcmp(argv[cur_arg + uii], "NA", 2))) {
 	    if (annot_info.modifier & ANNOT_PRUNE) {
-              logprint("Error: --annotate 'NA' and 'prune' cannot be used together.\n");
+              logerrprint("Error: --annotate 'NA' and 'prune' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    annot_info.modifier |= ANNOT_NA;
 	  } else if ((ujj == 5) && (!memcmp(argv[cur_arg + uii], "prune", 5))) {
 	    if (annot_info.modifier & ANNOT_NA) {
-              logprint("Error: --annotate 'NA' and 'prune' cannot be used together.\n");
+              logerrprint("Error: --annotate 'NA' and 'prune' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    annot_info.modifier |= ANNOT_PRUNE;
@@ -4466,17 +4510,17 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	}
 	if ((annot_info.modifier & ANNOT_BLOCK) && (annot_info.modifier & (ANNOT_NA | ANNOT_MINIMAL))) {
-	  logprint("Error: --annotate 'block' cannot be used with 'NA' or 'minimal'.\n");
+	  logerrprint("Error: --annotate 'block' cannot be used with 'NA' or 'minimal'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (!annot_info.attrib_fname) {
 	  if (!annot_info.ranges_fname) {
-	    logprint("Error: --annotate must be used with 'attrib' and/or 'ranges'.\n");
+	    logerrprint("Error: --annotate must be used with 'attrib' and/or 'ranges'.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	} else if (!annot_info.ranges_fname) {
 	  if (annot_info.subset_fname) {
-	    logprint("Error: --annotate 'subset' modifier must be used with 'ranges'.\n");
+	    logerrprint("Error: --annotate 'subset' modifier must be used with 'ranges'.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if (annot_info.modifier & (ANNOT_MINIMAL | ANNOT_DISTANCE)) {
 	    sprintf(logbuf, "Error: --annotate '%s' modifier must be used with 'ranges'.\n", (annot_info.modifier & ANNOT_MINIMAL)? "minimal" : "distance");
@@ -4485,7 +4529,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "nnotate-snp-field", 18)) {
 	if (!annot_info.attrib_fname) {
-	  logprint("Error: --annotate-snp-field must be used with --annotate + 'attrib'.\n");
+	  logerrprint("Error: --annotate-snp-field must be used with --annotate + 'attrib'.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4550,7 +4594,7 @@ int32_t main(int32_t argc, char** argv) {
 	if (param_ct) {
 	  sptr = argv[cur_arg + 1];
 	  if (strlen(sptr) > (FNAMESIZE - 5)) {
-	    logprint("Error: --bfile parameter too long.\n");
+	    logerrprint("Error: --bfile parameter too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	} else {
@@ -4572,7 +4616,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --bed parameter too long.\n");
+	  logerrprint("Error: --bed parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(pedname, argv[cur_arg + 1]);
@@ -4585,7 +4629,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --bim parameter too long.\n");
+	  logerrprint("Error: --bim parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(mapname, argv[cur_arg + 1]);
@@ -4594,31 +4638,31 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (param_ct == 2) {
-	  logprint("Error: --bmerge must have exactly 1 or 3 parameters.\n");
+	  logerrprint("Error: --bmerge must have exactly 1 or 3 parameters.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	jj = strlen(argv[cur_arg + 1]);
 	if (param_ct == 3) {
 	  if (++jj > FNAMESIZE) {
-	    logprint("Error: --bmerge .bed filename too long.\n");
+	    logerrprint("Error: --bmerge .bed filename too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(mergename1, argv[cur_arg + 1], jj);
 	  jj = strlen(argv[cur_arg + 2]) + 1;
 	  if (jj > FNAMESIZE) {
-	    logprint("Error: --bmerge .bim filename too long.\n");
+	    logerrprint("Error: --bmerge .bim filename too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(mergename2, argv[cur_arg + 2], jj);
 	  jj = strlen(argv[cur_arg + 3]) + 1;
 	  if (jj > FNAMESIZE) {
-	    logprint("Error: --bmerge .fam filename too long.\n");
+	    logerrprint("Error: --bmerge .fam filename too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(mergename3, argv[cur_arg + 3], jj);
 	} else {
 	  if (jj > (FNAMESIZE - 5)) {
-	    logprint("Error: --bmerge filename prefix too long.\n");
+	    logerrprint("Error: --bmerge filename prefix too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(memcpya(mergename1, argv[cur_arg + 1], jj), ".bed", 5);
@@ -4647,31 +4691,31 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "perm")) {
 	    if (cluster.modifier & CLUSTER_CMH_MPERM) {
-	      logprint("Error: --bd 'mperm' and 'perm{-bd}' cannot be used together.\n");
+	      logerrprint("Error: --bd 'mperm' and 'perm{-bd}' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (cluster.modifier & CLUSTER_CMH_PERM_BD) {
-	      logprint("Error: --bd 'perm' and 'perm-bd' modifiers cannot be used together.\n");
+	      logerrprint("Error: --bd 'perm' and 'perm-bd' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    cluster.modifier |= CLUSTER_CMH_PERM;
 	  } else if (!strcmp(argv[cur_arg + uii], "perm-bd")) {
 	    if (cluster.modifier & CLUSTER_CMH_MPERM) {
-	      logprint("Error: --bd 'mperm' and 'perm{-bd}' cannot be used together.\n");
+	      logerrprint("Error: --bd 'mperm' and 'perm{-bd}' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((cluster.modifier & (CLUSTER_CMH_PERM | CLUSTER_CMH_PERM_BD)) == CLUSTER_CMH_PERM) {
-	      logprint("Error: --bd 'perm' and 'perm-bd' modifiers cannot be used together.\n");
+	      logerrprint("Error: --bd 'perm' and 'perm-bd' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (mtest_adjust) {
-	      logprint("Error: --bd 'perm-bd' mode cannot currently be used with --adjust.\n");
+	      logerrprint("Error: --bd 'perm-bd' mode cannot currently be used with --adjust.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    cluster.modifier |= CLUSTER_CMH_PERM_BD;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
 	    if (cluster.modifier & CLUSTER_CMH_PERM) {
-	      logprint("Error: --bd 'mperm' and 'perm{-bd}' cannot be used together.\n");
+	      logerrprint("Error: --bd 'mperm' and 'perm{-bd}' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (cluster.modifier & CLUSTER_CMH_MPERM) {
-	      logprint("Error: Duplicate --bd 'mperm' modifier.\n");
+	      logerrprint("Error: Duplicate --bd 'mperm' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &(cluster.cmh_mperm_val))) {
@@ -4684,7 +4728,7 @@ int32_t main(int32_t argc, char** argv) {
 	  } else if (!strcmp(argv[cur_arg + uii], "set-test")) {
 	    cluster.modifier |= CLUSTER_CMH_SET_TEST;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-            logprint("Error: Improper --bd mperm syntax.  (Use '--bd mperm=[value]'.)\n");
+            logerrprint("Error: Improper --bd mperm syntax.  (Use '--bd mperm=[value]'.)\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  } else {
             sprintf(logbuf, "Error: Invalid --bd parameter '%s'.\n", argv[cur_arg + uii]);
@@ -4717,7 +4761,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	uii = strlen(argv[cur_arg + 1]);
 	if (uii > FNAMESIZE - 1) {
-	  logprint("Error: --bcf filename too long.\n");
+	  logerrprint("Error: --bcf filename too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(pedname, argv[cur_arg + 1], uii + 1);
@@ -4731,7 +4775,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --bgen parameter too long.\n");
+	  logerrprint("Error: --bgen parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(pedname, argv[cur_arg + 1]);
@@ -4759,7 +4803,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_BLOCKS;
       } else if (!memcmp(argptr2,"locks-inform-frac", 17)) {
 	if (!(calculation_type & CALC_BLOCKS)) {
-	  logprint("Error: --blocks-inform-frac must be used with --blocks.\n");
+	  logerrprint("Error: --blocks-inform-frac must be used with --blocks.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4772,7 +4816,7 @@ int32_t main(int32_t argc, char** argv) {
 	ld_info.blocks_inform_frac = dxx;
       } else if (!memcmp(argptr2, "locks-max-kb", 13)) {
 	if (!(calculation_type & CALC_BLOCKS)) {
-	  logprint("Error: --blocks-max-kb must be used with --blocks.\n");
+	  logerrprint("Error: --blocks-max-kb must be used with --blocks.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4789,7 +4833,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "locks-min-maf", 14)) {
         if (!(calculation_type & CALC_BLOCKS)) {
-	  logprint("Error: --blocks-min-maf must be used with --blocks.\n");
+	  logerrprint("Error: --blocks-min-maf must be used with --blocks.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4802,7 +4846,7 @@ int32_t main(int32_t argc, char** argv) {
         ld_info.blocks_min_maf = dxx;
       } else if (!memcmp(argptr2, "locks-recomb-highci", 20)) {
         if (!(calculation_type & CALC_BLOCKS)) {
-	  logprint("Error: --blocks-recomb-highci must be used with --blocks.\n");
+	  logerrprint("Error: --blocks-recomb-highci must be used with --blocks.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4814,13 +4858,13 @@ int32_t main(int32_t argc, char** argv) {
 	}
         ld_info.blocks_recomb_highci = ((int32_t)((dxx + SMALL_EPSILON) * 100));
 	if (ld_info.blocks_recomb_highci < 2) {
-	  logprint("Error: --blocks-recomb-highci parameter must be at least 0.02.\n");
+	  logerrprint("Error: --blocks-recomb-highci parameter must be at least 0.02.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	ld_info.blocks_recomb_highci--;
       } else if (!memcmp(argptr2, "locks-strong-highci", 20)) {
         if (!(calculation_type & CALC_BLOCKS)) {
-	  logprint("Error: --blocks-strong-highci must be used with --blocks.\n");
+	  logerrprint("Error: --blocks-strong-highci must be used with --blocks.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4834,12 +4878,12 @@ int32_t main(int32_t argc, char** argv) {
 	// 0.83 lower bound is needed for now for sane handling of size-2
 	// special case
 	if (ld_info.blocks_strong_highci < 83) {
-	  logprint("Error: --blocks-strong-highci parameter currently must be larger than 0.83.\nContact the developers if this is problematic.\n");
+	  logerrprint("Error: --blocks-strong-highci parameter currently must be larger than 0.83.\nContact the developers if this is problematic.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "locks-strong-lowci", 19)) {
         if (!(calculation_type & CALC_BLOCKS)) {
-	  logprint("Error: --blocks-strong-lowci must be used with --blocks.\n");
+	  logerrprint("Error: --blocks-strong-lowci must be used with --blocks.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -4852,14 +4896,14 @@ int32_t main(int32_t argc, char** argv) {
 	ld_info.blocks_strong_lowci_outer = 2 + (int32_t)((dxx - SMALL_EPSILON) * 100);
 	ld_info.blocks_strong_lowci = 2 + (int32_t)((dxx + SMALL_EPSILON) * 100);
 	if ((ld_info.blocks_strong_lowci_outer < 52) || (ld_info.blocks_strong_lowci > 82)) {
-	  logprint("Error: --blocks-strong-lowci parameter currently must be in (0.5, 0.81).\nContact the developers if this is problematic.\n");
+	  logerrprint("Error: --blocks-strong-lowci parameter currently must be in (0.5, 0.81).\nContact the developers if this is problematic.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "order", 6)) {
 	if (!annot_info.ranges_fname) {
-	  logprint("Error: --border now must be used with --annotate + 'ranges'.\n");
+	  logerrprint("Error: --border now must be used with --annotate + 'ranges'.\n");
 	  if (!annot_info.fname) {
-            logprint("Use --make-set-border with --make-set, etc.)\n");
+            logerrprint("Use --make-set-border with --make-set, etc.)\n");
 	  }
           goto main_ret_INVALID_CMDLINE_A;
 	}
@@ -4883,7 +4927,7 @@ int32_t main(int32_t argc, char** argv) {
     case 'c':
       if (!memcmp(argptr2, "hr", 3)) {
 	if (chrom_flag_present) {
-	  logprint("Error: --chr cannot be used with --autosome{-xy}.\n");
+	  logerrprint("Error: --chr cannot be used with --autosome{-xy}.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         retval = parse_chrom_ranges(param_ct, '-', &(argv[cur_arg]), chrom_info.chrom_mask, &chrom_info, (misc_flags / MISC_ALLOW_EXTRA_CHROMS) & 1, argptr);
@@ -4896,12 +4940,12 @@ int32_t main(int32_t argc, char** argv) {
 	logprint("Note: --compound-genotypes flag unnecessary (spaces between alleles in .ped\nand .lgen files are optional if all alleles are single-character).\n");
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ompress", 8)) {
-	logprint("Error: --compress flag retired.  Use e.g. 'gzip [filename]'.\n");
+	logerrprint("Error: --compress flag retired.  Use e.g. 'gzip [filename]'.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ounts", 6)) {
 	if (model_modifier & MODEL_ASSOC) {
 	  if (model_modifier & MODEL_QMASK) {
-	    logprint("Error: --assoc 'qt-means'/'lin' does not make sense with --counts.\n");
+	    logerrprint("Error: --assoc 'qt-means'/'lin' does not make sense with --counts.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  logprint("Note: --counts flag deprecated.  Use '--assoc counts' instead.\n");
@@ -4924,7 +4968,7 @@ int32_t main(int32_t argc, char** argv) {
             goto main_ret_INVALID_CMDLINE_WWA;
 	  }
 	  if (rplugin_fname) {
-	    logprint("Error: --covar 'keep-pheno-on-missing-cov' modifier cannot be used with --R.\n");
+	    logerrprint("Error: --covar 'keep-pheno-on-missing-cov' modifier cannot be used with --R.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
           covar_modifier |= COVAR_KEEP_PHENO_ON_MISSING_COV;
@@ -4935,7 +4979,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ovar-name", 10)) {
 	if (!covar_fname) {
-	  logprint("Error: --covar-name must be used with --covar.\n");
+	  logerrprint("Error: --covar-name must be used with --covar.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	retval = parse_name_ranges(param_ct, range_delim, &(argv[cur_arg]), &covar_range_list, 0);
@@ -4948,11 +4992,11 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (covar_modifier & COVAR_NAME) {
-	  logprint("Error: --covar-number cannot be used with --covar-name.\n");
+	  logerrprint("Error: --covar-number cannot be used with --covar-name.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (!covar_fname) {
-	  logprint("Error: --covar-number must be used with --covar.\n");
+	  logerrprint("Error: --covar-number must be used with --covar.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	retval = parse_name_ranges(param_ct, '-', &(argv[cur_arg]), &covar_range_list, 1);
@@ -4977,7 +5021,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((dxx < 0.01) || (dxx >= 1.0)) {
-	  logprint("Error: --ci confidence interval size s must satisfy 0.01 <= s < 1.\n");
+	  logerrprint("Error: --ci confidence interval size s must satisfy 0.01 <= s < 1.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	ci_size = dxx;
@@ -4990,7 +5034,7 @@ int32_t main(int32_t argc, char** argv) {
             cluster.modifier |= CLUSTER_CC;
 	  } else if (!strcmp(argv[cur_arg + uii], "group-avg")) {
 	    if (cluster.modifier & CLUSTER_OLD_TIEBREAKS) {
-              logprint("Error: --cluster 'group-avg' and 'old-tiebreaks' cannot be used together.\n");
+              logerrprint("Error: --cluster 'group-avg' and 'old-tiebreaks' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    cluster.modifier |= CLUSTER_GROUP_AVG;
@@ -5000,7 +5044,7 @@ int32_t main(int32_t argc, char** argv) {
 	    cluster.modifier |= CLUSTER_ONLY2;
 	  } else if (!strcmp(argv[cur_arg + uii], "old-tiebreaks")) {
 	    if (cluster.modifier & CLUSTER_GROUP_AVG) {
-              logprint("Error: --cluster 'group-avg' and 'old-tiebreaks' cannot be used together.\n");
+              logerrprint("Error: --cluster 'group-avg' and 'old-tiebreaks' cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    cluster.modifier |= CLUSTER_OLD_TIEBREAKS;
@@ -5016,7 +5060,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "luster-missing", 15)) {
 	if (calculation_type & CALC_CLUSTER) {
-	  logprint("Error: --cluster-missing cannot be used with --cluster.\n");
+	  logerrprint("Error: --cluster-missing cannot be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --cluster-missing flag deprecated.  Use '--cluster missing'.\n");
@@ -5029,7 +5073,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_INPUT_CONFLICT;
 	}
 	if (calculation_type & CALC_MERGE) {
-	  logprint("Error: --cfile cannot be used with --bmerge.\n");
+	  logerrprint("Error: --cfile cannot be used with --bmerge.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5038,7 +5082,7 @@ int32_t main(int32_t argc, char** argv) {
 	sptr = argv[cur_arg + 1];
 	uii = strlen(sptr);
 	if (uii > (FNAMESIZE - 9)) {
-	  logprint("Error: --cfile parameter too long.\n");
+	  logerrprint("Error: --cfile parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(memcpya(pedname, sptr, uii), ".cnv", 5);
@@ -5066,7 +5110,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-dup", 7)) {
 	UNSTABLE("cnv-dup");
 	if (cnv_calc_type & CNV_DEL) {
-	  logprint("Error: --cnv-dup cannot be used with --cnv-del.\n");
+	  logerrprint("Error: --cnv-dup cannot be used with --cnv-del.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	cnv_calc_type |= CNV_DUP;
@@ -5074,7 +5118,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-enrichment-test", 19)) {
 	UNSTABLE("cnv-enrichment-test");
 	if (!cnv_intersect_filter_type) {
-	  logprint("Error: --cnv-enrichment-test must be used with --cnv-count.\n");
+	  logerrprint("Error: --cnv-enrichment-test must be used with --cnv-count.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -5093,7 +5137,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (cnv_intersect_filter_type) {
-	  logprint("Error: --cnv-exclude cannot be used with --cnv-count.\n");
+	  logerrprint("Error: --cnv-exclude cannot be used with --cnv-count.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	retval = alloc_fname(&cnv_intersect_filter_fname, argv[cur_arg + 1], argptr, 0);
@@ -5118,7 +5162,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-freq-exclude-below", 22)) {
 	UNSTABLE("cnv-freq-exclude-below");
 	if (cnv_freq_type) {
-	  logprint("Error: --cnv-freq-exclude-below cannot be used with --cnv-freq-exclude-above.\n");
+	  logerrprint("Error: --cnv-freq-exclude-below cannot be used with --cnv-freq-exclude-above.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5132,7 +5176,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-freq-exclude-exact", 22)) {
 	UNSTABLE("cnv-freq-exclude-exact");
 	if (cnv_freq_type) {
-	  logprint("Error: --cnv-freq-exclude-exact cannot be used with\n--cnv-freq-exclude-above/-below.\n");
+	  logerrprint("Error: --cnv-freq-exclude-exact cannot be used with\n--cnv-freq-exclude-above/-below.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5146,7 +5190,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-freq-include-exact", 22)) {
 	UNSTABLE("cnv-freq-include-exact");
 	if (cnv_freq_type) {
-	  logprint("Error: --cnv-freq-include-exact cannot be used with\n--cnv-freq-exclude-above/-below/-exact.\n");
+	  logerrprint("Error: --cnv-freq-include-exact cannot be used with\n--cnv-freq-exclude-above/-below/-exact.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5176,10 +5220,10 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-freq-overlap", 16)) {
 	UNSTABLE("cnv-freq-overlap");
 	if (!(cnv_freq_type & CNV_FREQ_FILTER)) {
-	  logprint("Error: --cnv-freq-overlap must be used with --cnv-freq-include-exact or\n--cnv-freq-exclude-above/-below/-exact.\n");
+	  logerrprint("Error: --cnv-freq-overlap must be used with --cnv-freq-include-exact or\n--cnv-freq-exclude-above/-below/-exact.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (cnv_freq_type & CNV_FREQ_METHOD2) {
-	  logprint("Error: --cnv-freq-overlap cannot be used with --cnv-freq-method2.\n");
+	  logerrprint("Error: --cnv-freq-overlap cannot be used with --cnv-freq-method2.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -5213,7 +5257,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (cnv_intersect_filter_type) {
-	  logprint("Error: --cnv-intersect cannot be used with --cnv-count/--cnv-exclude.\n");
+	  logerrprint("Error: --cnv-intersect cannot be used with --cnv-count/--cnv-exclude.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	retval = alloc_fname(&cnv_intersect_filter_fname, argv[cur_arg + 1], argptr, 0);
@@ -5237,7 +5281,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_INPUT_CONFLICT;
 	}
 	if (calculation_type & CALC_MERGE) {
-	  logprint("Error: --cnv-list cannot be used with --bmerge.\n");
+	  logerrprint("Error: --cnv-list cannot be used with --bmerge.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5248,7 +5292,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-make-map", 12)) {
 	UNSTABLE("cnv-make-map");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-make-map cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-make-map cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -5266,7 +5310,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-max-kb", 10)) {
 	UNSTABLE("cnv-max-kb");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-max-kb cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-max-kb cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5278,13 +5322,13 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	cnv_max_seglen = (int32_t)(dxx * 1000 * (1 + SMALL_EPSILON));
 	if (cnv_min_seglen > cnv_max_seglen) {
-	  logprint("Error: --cnv-max-kb value cannot be smaller than --cnv-kb value.\n");
+	  logerrprint("Error: --cnv-max-kb value cannot be smaller than --cnv-kb value.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "nv-max-score", 13)) {
 	UNSTABLE("cnv-max-score");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-max-score cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-max-score cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5297,7 +5341,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-max-sites", 13)) {
 	UNSTABLE("cnv-max-sites");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-max-sites cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-max-sites cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5310,10 +5354,10 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-overlap", 11)) {
 	UNSTABLE("cnv-overlap");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-overlap cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-overlap cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (cnv_overlap_type == CNV_DISRUPT) {
-	  logprint("Error: --cnv-overlap cannot be used with --cnv-disrupt.\n");
+	  logerrprint("Error: --cnv-overlap cannot be used with --cnv-disrupt.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5337,10 +5381,10 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-region-overlap", 18)) {
 	UNSTABLE("cnv-region-overlap");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-region-overlap cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-region-overlap cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (cnv_overlap_type) {
-	  logprint("Error: --cnv-region-overlap cannot be used with --cnv-overlap/-disrupt.\n");
+	  logerrprint("Error: --cnv-region-overlap cannot be used with --cnv-overlap/-disrupt.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5354,7 +5398,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-score", 9)) {
 	UNSTABLE("cnv-score");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-score cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-score cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5365,13 +5409,13 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (cnv_min_score > cnv_max_score) {
-	  logprint("Error: --cnv-score value cannot be greater than --cnv-max-score value.\n");
+	  logerrprint("Error: --cnv-score value cannot be greater than --cnv-max-score value.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "nv-sites", 9)) {
 	UNSTABLE("cnv-sites");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-sites cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-sites cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5382,16 +5426,16 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (cnv_min_sites > cnv_max_sites) {
-	  logprint("Error: --cnv-sites value cannot be greater than --cnv-max-sites value.\n");
+	  logerrprint("Error: --cnv-sites value cannot be greater than --cnv-max-sites value.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "nv-subset", 10)) {
 	UNSTABLE("cnv-subset");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-subset cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-subset cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (!cnv_intersect_filter_type) {
-	  logprint("Error: --cnv-subset must be used with --cnv-intersect/-exclude/-count.\n");
+	  logerrprint("Error: --cnv-subset must be used with --cnv-intersect/-exclude/-count.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5404,7 +5448,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-test", 8)) {
 	UNSTABLE("cnv-test");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-test cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-test cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -5424,7 +5468,7 @@ int32_t main(int32_t argc, char** argv) {
 	    } else if (!strcmp(argv[cur_arg + 2], "2sided")) {
 	      cnv_calc_type |= CNV_TEST_FORCE_2SIDED;
 	    } else {
-	      logprint("Error: Invalid --cnv-test parameter sequence.\n");
+	      logerrprint("Error: Invalid --cnv-test parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	  }
@@ -5439,7 +5483,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-test-1sided", 15)) {
 	UNSTABLE("cnv-test-1sided");
 	if (cnv_calc_type & CNV_TEST_FORCE_2SIDED) {
-	  logprint("Error: --cnv-test cannot be both 1-sided and 2-sided at the same time.\n");
+	  logerrprint("Error: --cnv-test cannot be both 1-sided and 2-sided at the same time.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --cnv-test-1sided flag deprecated.  Use '--cnv-test 1sided'.\n");
@@ -5447,7 +5491,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-test-2sided", 15)) {
 	UNSTABLE("cnv-test-2sided");
 	if (cnv_calc_type & CNV_TEST_FORCE_1SIDED) {
-	  logprint("Error: --cnv-test cannot be both 1-sided and 2-sided at the same time.\n");
+	  logerrprint("Error: --cnv-test cannot be both 1-sided and 2-sided at the same time.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --cnv-test-2sided flag deprecated.  Use '--cnv-test 2sided'.\n");
@@ -5455,7 +5499,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-test-region", 15)) {
 	UNSTABLE("cnv-test-region");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-test-region cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-test-region cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -5471,7 +5515,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-test-window", 15)) {
 	UNSTABLE("cnv-test-window");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-test-window cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-test-window cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5490,10 +5534,10 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-union-overlap", 17)) {
 	UNSTABLE("cnv-union-overlap");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-union-overlap cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-union-overlap cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (cnv_overlap_type) {
-	  logprint("Error: --cnv-union-overlap cannot be used with --cnv-{region-}overlap/-disrupt.\n");
+	  logerrprint("Error: --cnv-union-overlap cannot be used with --cnv-{region-}overlap/-disrupt.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5507,7 +5551,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-write", 9)) {
 	UNSTABLE("cnv-write");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-write cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-write cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -5519,7 +5563,7 @@ int32_t main(int32_t argc, char** argv) {
 	    goto main_ret_INVALID_CMDLINE_WWA;
 	  }
 	  if (!(cnv_freq_val & CNV_FREQ_METHOD2)) {
-	    logprint("Error: --cnv-write 'freq' modifier must be used with --cnv-freq-method2.\n");
+	    logerrprint("Error: --cnv-write 'freq' modifier must be used with --cnv-freq-method2.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  cnv_calc_type |= CNV_WRITE_FREQ;
@@ -5528,13 +5572,13 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "nv-write-freq", 14)) {
 	UNSTABLE("cnv-write-freq");
 	if (!(load_rare & LOAD_RARE_CNV)) {
-	  logprint("Error: --cnv-write freq cannot be used without a .cnv fileset.\n");
+	  logerrprint("Error: --cnv-write freq cannot be used without a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (!(cnv_freq_val & CNV_FREQ_METHOD2)) {
-	  logprint("Error: --cnv-write 'freq' modifier must be used with --cnv-freq-method2.\n");
+	  logerrprint("Error: --cnv-write 'freq' modifier must be used with --cnv-freq-method2.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (!(cnv_calc_type & CNV_WRITE)) {
-	  logprint("Error: --cnv-write-freq must be used with --cnv-write.\n");
+	  logerrprint("Error: --cnv-write-freq must be used with --cnv-write.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --cnv-write-freq flag deprecated.  Use '--cnv-write freq'.\n");
@@ -5560,7 +5604,7 @@ int32_t main(int32_t argc, char** argv) {
 	    } else if (!strcmp("recessive", argv[cur_arg + 1])) {
 	      glm_modifier |= GLM_CONDITION_RECESSIVE;
 	    } else {
-	      logprint("Error: Invalid --condition parameter sequence.\n");
+	      logerrprint("Error: Invalid --condition parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	  }
@@ -5570,7 +5614,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ondition-list", 14)) {
 	if (condition_mname) {
-	  logprint("Error: --condition-list cannot be used with --condition.\n");
+	  logerrprint("Error: --condition-list cannot be used with --condition.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -5589,7 +5633,7 @@ int32_t main(int32_t argc, char** argv) {
 	    } else if (!strcmp("recessive", argv[cur_arg + 1])) {
 	      glm_modifier |= GLM_CONDITION_RECESSIVE;
 	    } else {
-	      logprint("Error: Invalid --condition-list parameter sequence.\n");
+	      logerrprint("Error: Invalid --condition-list parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	  }
@@ -5620,11 +5664,11 @@ int32_t main(int32_t argc, char** argv) {
 	  // must contain exactly one '@'
           sptr = strchr(argv[cur_arg + 1], '@');
 	  if (!sptr) {
-            logprint("Error: --cm-map requires either a '@' in the filename pattern, or a chromosome\ncode as the second parameter.\n");
+            logerrprint("Error: --cm-map requires either a '@' in the filename pattern, or a chromosome\ncode as the second parameter.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
           if (strchr(&(sptr[1]), '@')) {
-	    logprint("Error: Multiple '@'s in --cm-map filename pattern.\n");
+	    logerrprint("Error: Multiple '@'s in --cm-map filename pattern.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
           if (alloc_string(&cm_map_fname, argv[cur_arg + 1])) {
@@ -5673,27 +5717,27 @@ int32_t main(int32_t argc, char** argv) {
 		}
 	      } else if (ujj == 2) {
 		if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_f_yobs)) {
-		  logprint("Error: Invalid --check-sex female Ychr maximum nonmissing genotype count.\n");
+		  logerrprint("Error: Invalid --check-sex female Ychr maximum nonmissing genotype count.\n");
 		  goto main_ret_INVALID_CMDLINE_A;
 		}
 	      } else if (ujj == 3) {
 		if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_m_yobs)) {
-		  logprint("Error: Invalid --check-sex male Ychr minimum nonmissing genotype count.\n");
+		  logerrprint("Error: Invalid --check-sex male Ychr minimum nonmissing genotype count.\n");
 		  goto main_ret_INVALID_CMDLINE_A;
 		}
 	      } else {
-		logprint("Error: Invalid --check-sex parameter sequence.\n");
+		logerrprint("Error: Invalid --check-sex parameter sequence.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	      ujj++;
 	    }
 	  }
 	  if ((ujj > 2) && (!(misc_flags & MISC_SEXCHECK_YCOUNT))) {
-	    logprint("Error: --check-sex only accepts >2 numeric parameters in 'ycount' mode.\n");
+	    logerrprint("Error: --check-sex only accepts >2 numeric parameters in 'ycount' mode.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  if (check_sex_fthresh > check_sex_mthresh) {
-	    logprint("Error: --check-sex female F estimate ceiling cannot be larger than male floor.\n");
+	    logerrprint("Error: --check-sex female F estimate ceiling cannot be larger than male floor.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  // actually fine if check_sex_f_yobs > check_sex_m_yobs
@@ -5708,27 +5752,27 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	    // may as well print a more informative error message in this case
 	    if (!strcmp(argv[cur_arg + uii], "ycount")) {
-	      logprint("Error: Conflicting --check-sex modes.\n");
+	      logerrprint("Error: Conflicting --check-sex modes.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (!ukk) {
 	      if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_f_yobs)) {
-		logprint("Error: Invalid --check-sex y-only female Ychr maximum nonmissing genotype\ncount.\n");
+		logerrprint("Error: Invalid --check-sex y-only female Ychr maximum nonmissing genotype\ncount.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	    } else if (ukk == 1) {
 	      if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_m_yobs)) {
-		logprint("Error: Invalid --check-sex y-only male Ychr minimum nonmissing genotype count.\n");
+		logerrprint("Error: Invalid --check-sex y-only male Ychr minimum nonmissing genotype count.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	    } else {
-	      logprint("Error: Invalid --check-sex y-only parameter sequence.\n");
+	      logerrprint("Error: Invalid --check-sex y-only parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    ukk++;
 	  }
 	  if (check_sex_f_yobs >= check_sex_m_yobs) {
-	    logprint("Error: In y-only mode, --check-sex female Y observation threshold must be\nsmaller than male threshold.\n");
+	    logerrprint("Error: In y-only mode, --check-sex female Y observation threshold must be\nsmaller than male threshold.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  misc_flags |= MISC_SEXCHECK_YCOUNT | MISC_SEXCHECK_YONLY;
@@ -5751,14 +5795,14 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-allow-overlap", 19)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-allow-overlap must be used with --clump.\n");
+	  logerrprint("Error: --clump-allow-overlap must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
         clump_info.modifier |= CLUMP_ALLOW_OVERLAP;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "lump-annotate", 14)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-annotate must be used with --clump.\n");
+	  logerrprint("Error: --clump-annotate must be used with --clump.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x7fffffff)) {
@@ -5770,14 +5814,14 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-best", 10)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-best must be used with --clump.\n");
+	  logerrprint("Error: --clump-best must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
         clump_info.modifier |= CLUMP_BEST;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "lump-field", 11)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-field must be used with --clump.\n");
+	  logerrprint("Error: --clump-field must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x7fffffff)) {
@@ -5789,17 +5833,17 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-index-first", 17)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-index-first must be used with --clump.\n");
+	  logerrprint("Error: --clump-index-first must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	} else if (clump_info.fname_ct == 1) {
-	  logprint("Warning: --clump-index-first has no effect when there is only one --clump file.\n");
+	  logerrprint("Warning: --clump-index-first has no effect when there is only one --clump file.\n");
 	} else {
           clump_info.modifier |= CLUMP_INDEX_FIRST;
 	}
         goto main_param_zero;
       } else if (!memcmp(argptr2, "lump-kb", 8)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-kb must be used with --clump.\n");
+	  logerrprint("Error: --clump-kb must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5817,7 +5861,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-p1", 8)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-p1 must be used with --clump.\n");
+	  logerrprint("Error: --clump-p1 must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5830,7 +5874,7 @@ int32_t main(int32_t argc, char** argv) {
 	clump_info.p1 = dxx;
       } else if (!memcmp(argptr2, "lump-p2", 8)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-p2 must be used with --clump.\n");
+	  logerrprint("Error: --clump-p2 must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5843,7 +5887,7 @@ int32_t main(int32_t argc, char** argv) {
 	clump_info.p2 = dxx;
       } else if (!memcmp(argptr2, "lump-r2", 8)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-r2 must be used with --clump.\n");
+	  logerrprint("Error: --clump-r2 must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5856,7 +5900,7 @@ int32_t main(int32_t argc, char** argv) {
 	clump_info.r2 = dxx;
       } else if (!memcmp(argptr2, "lump-range", 11)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-range must be used with --clump.\n");
+	  logerrprint("Error: --clump-range must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5868,7 +5912,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-range-border", 18)) {
 	if (!clump_info.range_fname) {
-	  logprint("Error: --clump-range-border must be used with --clump-range.\n");
+	  logerrprint("Error: --clump-range-border must be used with --clump-range.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -5885,7 +5929,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-replicate", 15)) {
         if (clump_info.fname_ct < 2) {
-	  logprint("Error: --clump-replicate requires multiple --clump files.\n");
+	  logerrprint("Error: --clump-replicate requires multiple --clump files.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (clump_info.modifier & CLUMP_BEST) {
@@ -5896,7 +5940,7 @@ int32_t main(int32_t argc, char** argv) {
         goto main_param_zero;
       } else if (!memcmp(argptr2, "lump-snp-field", 15)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-snp-field must be used with --clump.\n");
+	  logerrprint("Error: --clump-snp-field must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x7fffffff)) {
@@ -5908,7 +5952,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lump-verbose", 13)) {
         if (!clump_info.fname_ct) {
-	  logprint("Error: --clump-verbose must be used with --clump.\n");
+	  logerrprint("Error: --clump-verbose must be used with --clump.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
         clump_info.modifier |= CLUMP_VERBOSE;
@@ -5934,7 +5978,7 @@ int32_t main(int32_t argc, char** argv) {
 	if (param_ct) {
 	  sptr = argv[cur_arg + 1];
 	  if (strlen(sptr) > (FNAMESIZE - 8)) {
-	    logprint("Error: --data parameter too long.\n");
+	    logerrprint("Error: --data parameter too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	} else {
@@ -5948,7 +5992,7 @@ int32_t main(int32_t argc, char** argv) {
 	memcpy(strcpya(mapname, sptr), ".sample", 8);
 	load_params |= LOAD_PARAMS_OXSAMPLE;
       } else if (!memcmp(argptr2, "ecompress", 10)) {
-	logprint("Error: --decompress flag retired.  Use e.g. 'gunzip [filename]'.\n");
+	logerrprint("Error: --decompress flag retired.  Use e.g. 'gunzip [filename]'.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "istance", 8)) {
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 6)) {
@@ -5957,64 +6001,64 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "square")) {
 	    if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_SQ0) {
-	      logprint("Error: --distance 'square' and 'square0' modifiers cannot coexist.\n");
+	      logerrprint("Error: --distance 'square' and 'square0' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_TRI) {
-	      logprint("Error: --distance 'square' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --distance 'square' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dist_calc_type |= DISTANCE_SQ;
 	  } else if (!strcmp(argv[cur_arg + uii], "square0")) {
 	    if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_SQ) {
-	      logprint("Error: --distance 'square' and 'square0' modifiers cannot coexist.\n");
+	      logerrprint("Error: --distance 'square' and 'square0' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_TRI) {
-	      logprint("Error: --distance 'square0' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --distance 'square0' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dist_calc_type |= DISTANCE_SQ0;
 	  } else if (!strcmp(argv[cur_arg + uii], "triangle")) {
 	    if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_SQ) {
-	      logprint("Error: --distance 'square' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --distance 'square' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_SQ0) {
-	      logprint("Error: --distance 'square0' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --distance 'square0' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dist_calc_type |= DISTANCE_TRI;
 	  } else if (!strcmp(argv[cur_arg + uii], "gz")) {
 	    if (dist_calc_type & (DISTANCE_BIN | DISTANCE_BIN4)) {
-	      logprint("Error: Conflicting --distance modifiers.\n");
+	      logerrprint("Error: Conflicting --distance modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dist_calc_type |= DISTANCE_GZ;
 	  } else if (!strcmp(argv[cur_arg + uii], "bin")) {
 	    if (dist_calc_type & (DISTANCE_GZ | DISTANCE_BIN4)) {
-	      logprint("Error: Conflicting --distance modifiers.\n");
+	      logerrprint("Error: Conflicting --distance modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dist_calc_type |= DISTANCE_BIN;
 	  } else if (!strcmp(argv[cur_arg + uii], "bin4")) {
 	    if (dist_calc_type & (DISTANCE_GZ | DISTANCE_BIN)) {
-	      logprint("Error: Conflicting --distance modifiers.\n");
+	      logerrprint("Error: Conflicting --distance modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dist_calc_type |= DISTANCE_BIN4;
 	  } else if (!strcmp(argv[cur_arg + uii], "ibs")) {
 	    if (dist_calc_type & DISTANCE_IBS) {
-	      logprint("Error: Duplicate --distance 'ibs' modifier.\n");
+	      logerrprint("Error: Duplicate --distance 'ibs' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dist_calc_type |= DISTANCE_IBS;
 	  } else if (!strcmp(argv[cur_arg + uii], "1-ibs")) {
 	    if (dist_calc_type & DISTANCE_1_MINUS_IBS) {
-	      logprint("Error: Duplicate --distance '1-ibs' modifier.\n");
+	      logerrprint("Error: Duplicate --distance '1-ibs' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dist_calc_type |= DISTANCE_1_MINUS_IBS;
 	  } else if (!strcmp(argv[cur_arg + uii], "allele-ct")) {
 	    if (dist_calc_type & DISTANCE_ALCT) {
-	      logprint("Error: Duplicate --distance 'allele-ct' modifier.\n");
+	      logerrprint("Error: Duplicate --distance 'allele-ct' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dist_calc_type |= DISTANCE_ALCT;
@@ -6040,10 +6084,10 @@ int32_t main(int32_t argc, char** argv) {
 	fputs("Note: '--distance-exp [x]' deprecated.  Use '--distance-weights exp=[x]' instead.\n", stdout);
       } else if (!memcmp(argptr2, "istance-wts", 12)) {
 	if (distance_exp != 0.0) {
-	  logprint("Error: --distance-wts cannot be used with --distance-exp.\n");
+	  logerrprint("Error: --distance-wts cannot be used with --distance-exp.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (calculation_type & CALC_PLINK1_DISTANCE_MATRIX) {
-	  logprint("Error: --distance-wts cannot be used with --distance-matrix.\n");
+	  logerrprint("Error: --distance-wts cannot be used with --distance-matrix.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -6073,11 +6117,11 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "istance-matrix", 15)) {
 	if (distance_exp != 0.0) {
-	  logprint("Error: --distance-matrix cannot be used with --distance-exp.\n");
+	  logerrprint("Error: --distance-matrix cannot be used with --distance-exp.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (dist_calc_type & DISTANCE_1_MINUS_IBS) {
-	  logprint("Error: --distance-matrix flag cannot be used with '--distance 1-ibs'.\n");
+	  logerrprint("Error: --distance-matrix flag cannot be used with '--distance 1-ibs'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	calculation_type |= CALC_PLINK1_DISTANCE_MATRIX;
@@ -6090,29 +6134,29 @@ int32_t main(int32_t argc, char** argv) {
           goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (scan_posint_defcap(argv[cur_arg + 1], &dummy_sample_ct)) {
-	  logprint("Error: Invalid --dummy sample count.\n");
+	  logerrprint("Error: Invalid --dummy sample count.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (scan_posint_defcap(argv[cur_arg + 2], &dummy_marker_ct)) {
-	  logprint("Error: Invalid --dummy variant count.\n");
+	  logerrprint("Error: Invalid --dummy variant count.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         for (uii = 3; uii <= param_ct; uii++) {
 	  if (match_upper(argv[cur_arg + uii], "ACGT")) {
 	    if (dummy_flags & (DUMMY_1234 | DUMMY_12)) {
-	      logprint("Error: --dummy 'acgt' modifier cannot be used with '1234' or '12'.\n");
+	      logerrprint("Error: --dummy 'acgt' modifier cannot be used with '1234' or '12'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
             dummy_flags |= DUMMY_ACGT;
 	  } else if (!strcmp(argv[cur_arg + uii], "1234")) {
 	    if (dummy_flags & (DUMMY_ACGT | DUMMY_12)) {
-	      logprint("Error: --dummy '1234' modifier cannot be used with 'acgt' or '12'.\n");
+	      logerrprint("Error: --dummy '1234' modifier cannot be used with 'acgt' or '12'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
             dummy_flags |= DUMMY_1234;
 	  } else if (!strcmp(argv[cur_arg + uii], "12")) {
 	    if (dummy_flags & (DUMMY_ACGT | DUMMY_1234)) {
-	      logprint("Error: --dummy '12' modifier cannot be used with 'acgt' or '1234'.\n");
+	      logerrprint("Error: --dummy '12' modifier cannot be used with 'acgt' or '1234'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
             dummy_flags |= DUMMY_12;
@@ -6134,7 +6178,7 @@ int32_t main(int32_t argc, char** argv) {
 	load_rare = LOAD_RARE_DUMMY;
       } else if (!memcmp(argptr2, "ummy-coding", 12)) {
 	if (!covar_fname) {
-	  logprint("Error: --dummy-coding cannot be used without --covar.\n");
+	  logerrprint("Error: --dummy-coding cannot be used without --covar.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 2)) {
@@ -6147,7 +6191,7 @@ int32_t main(int32_t argc, char** argv) {
 	  } else {
 	    if (param_ct == 2) {
               if (strcmp(argv[cur_arg + 2], "no-round")) {
-		logprint("Error: Invalid --dummy-coding parameter sequence.\n");
+		logerrprint("Error: Invalid --dummy-coding parameter sequence.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	      write_covar_modifier |= WRITE_COVAR_DUMMY_NO_ROUND;
@@ -6169,7 +6213,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ouble-id", 9)) {
         if (const_fid) {
-	  logprint("Error: --double-id cannot be used with --const-fid.\n");
+	  logerrprint("Error: --double-id cannot be used with --const-fid.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         misc_flags |= MISC_DOUBLE_ID;
@@ -6178,16 +6222,16 @@ int32_t main(int32_t argc, char** argv) {
 	if (load_rare || load_params) {
 	  goto main_ret_INVALID_CMDLINE_INPUT_CONFLICT;
 	} else if (pheno_modifier & PHENO_ALL) {
-	  logprint("Error: --dosage cannot be used with --all-pheno.\n");
+	  logerrprint("Error: --dosage cannot be used with --all-pheno.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (mtest_adjust) {
-	  logprint("Error: --dosage cannot be used with --adjust.\n");
+	  logerrprint("Error: --dosage cannot be used with --adjust.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (covar_modifier & COVAR_KEEP_PHENO_ON_MISSING_COV) {
-	  logprint("Error: --dosage cannot be used with --covar 'keep-pheno-on-missing-cov'\nmodifier.\n");
+	  logerrprint("Error: --dosage cannot be used with --covar 'keep-pheno-on-missing-cov'\nmodifier.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (condition_mname || condition_fname) {
-	  logprint("Error: --dosage does not support --condition/--condition-list.\n");
+	  logerrprint("Error: --dosage does not support --condition/--condition-list.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 13)) {
@@ -6217,19 +6261,19 @@ int32_t main(int32_t argc, char** argv) {
 	    dosage_info.modifier |= DOSAGE_FREQ_CC;
 	  } else if (!strcmp(argv[cur_arg + uii], "frq2")) {
 	    // turn this into an error before official 1.90 release
-	    logprint("Warning: The --dosage 'frq2' modifier has been renamed to 'case-control-freqs'.\n");
+	    logerrprint("Warning: The --dosage 'frq2' modifier has been renamed to 'case-control-freqs'.\n");
 	    dosage_info.modifier |= DOSAGE_FREQ_CC;
 	  } else if (strlen(argv[cur_arg + uii]) <= 6) {
 	    goto main_dosage_invalid_param;
 	  } else if (!strcmp(argv[cur_arg + uii], "sepheader")) {
 	    if (dosage_info.modifier & DOSAGE_NOHEADER) {
-	      logprint("Error: --dosage 'sepheader' and 'noheader' modifiers cannot be used together.\n");
+	      logerrprint("Error: --dosage 'sepheader' and 'noheader' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dosage_info.modifier |= DOSAGE_SEPHEADER;
 	  } else if (!strcmp(argv[cur_arg + uii], "noheader")) {
 	    if (dosage_info.modifier & DOSAGE_SEPHEADER) {
-	      logprint("Error: --dosage 'sepheader' and 'noheader' modifiers cannot be used together.\n");
+	      logerrprint("Error: --dosage 'sepheader' and 'noheader' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    dosage_info.modifier |= DOSAGE_NOHEADER;
@@ -6265,18 +6309,18 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	if (dosage_info.modifier & DOSAGE_OCCUR) {
 	  if ((glm_modifier & GLM_STANDARD_BETA) || (dosage_info.modifier & DOSAGE_SEX)) {
-	    logprint("Error: --dosage 'occur' mode cannot be used with association analysis\nmodifiers/flags.\n");
+	    logerrprint("Error: --dosage 'occur' mode cannot be used with association analysis\nmodifiers/flags.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
 	} else {
 	  dosage_info.modifier |= DOSAGE_GLM;
 	}
 	if ((dosage_info.modifier & (DOSAGE_LIST | DOSAGE_SEPHEADER)) == DOSAGE_SEPHEADER) {
-	  logprint("Error: --dosage 'sepheader' modifier must be used with 'list'.\n");
+	  logerrprint("Error: --dosage 'sepheader' modifier must be used with 'list'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((dosage_info.modifier & DOSAGE_DOSE1) && (dosage_info.format != 1)) {
-	  logprint("Error: --dosage 'dose1' modifier must be used with 'format=1'.\n");
+	  logerrprint("Error: --dosage 'dose1' modifier must be used with 'format=1'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	load_rare = LOAD_RARE_DOSAGE;
@@ -6290,7 +6334,7 @@ int32_t main(int32_t argc, char** argv) {
 	    family_info.dfam_modifier |= DFAM_NO_UNRELATEDS;
 	  } else if (!strcmp(argv[cur_arg + uii], "perm")) {
 	    if (family_info.dfam_modifier & DFAM_MPERM) {
-	      logprint("Error: --dfam 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --dfam 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.dfam_modifier |= DFAM_PERM;
@@ -6298,10 +6342,10 @@ int32_t main(int32_t argc, char** argv) {
 	    family_info.dfam_modifier |= DFAM_PERM_COUNT;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
 	    if (family_info.dfam_modifier & DFAM_PERM) {
-	      logprint("Error: --dfam 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --dfam 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (family_info.dfam_modifier & DFAM_MPERM) {
-	      logprint("Error: Duplicate --dfam 'mperm' modifier.\n");
+	      logerrprint("Error: Duplicate --dfam 'mperm' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &family_info.dfam_mperm_val)) {
@@ -6312,7 +6356,7 @@ int32_t main(int32_t argc, char** argv) {
 	  } else if (!strcmp(argv[cur_arg + uii], "set-test")) {
 	    family_info.dfam_modifier |= DFAM_SET_TEST;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-	    logprint("Error: Improper --dfam mperm syntax.  (Use '--dfam mperm=[value]'.)\n");
+	    logerrprint("Error: Improper --dfam mperm syntax.  (Use '--dfam mperm=[value]'.)\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else {
 	    sprintf(logbuf, "Error: Invalid --dfam parameter '%s'.\n", argv[cur_arg + uii]);
@@ -6324,7 +6368,7 @@ int32_t main(int32_t argc, char** argv) {
 	// keep this undocumented flag since it makes DFAM correspond to the
 	// original sib-TDT.
 	if (!(calculation_type & CALC_DFAM)) {
-	  logprint("Error: --dfam-no-unrelateds must be used with --dfam.\n");
+	  logerrprint("Error: --dfam-no-unrelateds must be used with --dfam.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	family_info.dfam_modifier |= DFAM_NO_UNRELATEDS;
@@ -6349,7 +6393,7 @@ int32_t main(int32_t argc, char** argv) {
 	  if (!strcmp(argv[cur_arg + 1], "range")) {
             uii = 2;
 	  } else if (strcmp(argv[cur_arg + 2], "range")) {
-	    logprint("Error: Invalid --extract parameter sequence.\n");
+	    logerrprint("Error: Invalid --extract parameter sequence.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
           misc_flags |= MISC_EXTRACT_RANGE;
@@ -6368,7 +6412,7 @@ int32_t main(int32_t argc, char** argv) {
 	  if (!strcmp(argv[cur_arg + 1], "range")) {
             uii = 2;
 	  } else if (strcmp(argv[cur_arg + 2], "range")) {
-	    logprint("Error: Invalid --exclude parameter sequence.\n");
+	    logerrprint("Error: Invalid --exclude parameter sequence.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
           misc_flags |= MISC_EXCLUDE_RANGE;
@@ -6388,7 +6432,7 @@ int32_t main(int32_t argc, char** argv) {
         filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV | FILTER_EXCLUDE_MARKERNAME_SNP;
       } else if (!memcmp(argptr2, "xclude-snps", 12)) {
 	if (markername_snp) {
-	  logprint("Error: --exclude-snps cannot be used with --exclude-snp.\n");
+	  logerrprint("Error: --exclude-snps cannot be used with --exclude-snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	retval = parse_name_ranges(param_ct, range_delim, &(argv[cur_arg]), &snps_range_list, 0);
@@ -6398,7 +6442,7 @@ int32_t main(int32_t argc, char** argv) {
         filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV | FILTER_EXCLUDE_MARKERNAME_SNP;
       } else if (!memcmp(argptr2, "pistasis", 9)) {
 	if (epi_info.modifier & EPI_FAST_CASE_ONLY) {
-	  logprint("Error: --epistasis cannot be used with --case-only.\n");
+	  logerrprint("Error: --epistasis cannot be used with --case-only.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -6487,7 +6531,7 @@ int32_t main(int32_t argc, char** argv) {
 	if (param_ct) {
 	  sptr = argv[cur_arg + 1];
 	  if (strlen(sptr) > (FNAMESIZE - 5)) {
-	    logprint("Error: --file parameter too long.\n");
+	    logerrprint("Error: --file parameter too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	} else {
@@ -6504,7 +6548,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --fam parameter too long.\n");
+	  logerrprint("Error: --fam parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(famname, argv[cur_arg + 1]);
@@ -6526,7 +6570,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ilter-controls", 15)) {
 	if (filter_flags & FILTER_BINARY_CASES) {
-	  logprint("Error: --filter-cases and --filter-controls cannot be used together.\n");
+	  logerrprint("Error: --filter-cases and --filter-controls cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	filter_flags |= FILTER_FAM_REQ | FILTER_BINARY_CONTROLS;
@@ -6536,7 +6580,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ilter-males", 12)) {
 	if (filter_flags & FILTER_BINARY_FEMALES) {
-	  logprint("Error: --filter-males and --filter-females cannot be used together.\n");
+	  logerrprint("Error: --filter-males and --filter-females cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	filter_flags |= FILTER_FAM_REQ | FILTER_BINARY_MALES;
@@ -6546,7 +6590,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ilter-nonfounders", 18)) {
 	if (filter_flags & FILTER_BINARY_FOUNDERS) {
-	  logprint("Error: --filter-founders and --filter-nonfounders cannot be used together.\n");
+	  logerrprint("Error: --filter-founders and --filter-nonfounders cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	filter_flags |= FILTER_FAM_REQ | FILTER_BINARY_NONFOUNDERS;
@@ -6558,13 +6602,13 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "counts")) {
 	    if (misc_flags & MISC_FREQ_CC) {
-	      logprint("Error: --freq 'counts' and 'case-control' modifiers cannot be used together.\n");
+	      logerrprint("Error: --freq 'counts' and 'case-control' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    misc_flags |= MISC_FREQ_COUNTS;
 	  } else if (!strcmp(argv[cur_arg + uii], "case-control")) {
 	    if (misc_flags & MISC_FREQ_COUNTS) {
-	      logprint("Error: --freq 'counts' and 'case-control' modifiers cannot be used together.\n");
+	      logerrprint("Error: --freq 'counts' and 'case-control' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    misc_flags |= MISC_FREQ_CC;
@@ -6584,7 +6628,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "reqx", 5)) {
 	if (calculation_type & CALC_FREQ) {
-	  logprint("Error: --freqx cannot be used with --freq.\n");
+	  logerrprint("Error: --freqx cannot be used with --freq.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -6601,7 +6645,7 @@ int32_t main(int32_t argc, char** argv) {
 	misc_flags |= MISC_FREQX;
       } else if (!memcmp(argptr2, "rom", 4)) {
 	if (chrom_flag_present) {
-	  logprint("Error: --from cannot be used with --autosome{-xy} or --{not-}chr.\n");
+	  logerrprint("Error: --from cannot be used with --autosome{-xy} or --{not-}chr.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -6613,7 +6657,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV;
       } else if ((!memcmp(argptr2, "rom-bp", 7)) || (!memcmp(argptr2, "rom-kb", 7)) || (!memcmp(argptr2, "rom-mb", 7))) {
 	if (markername_from) {
-	  logprint("Error: --from-bp/-kb/-mb cannot be used with --from.\n");
+	  logerrprint("Error: --from-bp/-kb/-mb cannot be used with --from.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -6627,7 +6671,7 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	} else {
 	  if (marker_pos_start != -1) {
-	    logprint("Error: Multiple --from-bp/-kb/-mb values.\n");
+	    logerrprint("Error: Multiple --from-bp/-kb/-mb values.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  if (scan_double(argv[cur_arg + 1], &dxx)) {
@@ -6646,7 +6690,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP;
       } else if (!memcmp(argptr2, "isher", 6)) {
 	if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --fisher cannot be used with --assoc.\n");
+	  logerrprint("Error: --fisher cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --fisher flag deprecated.  Use '--assoc fisher' or '--model fisher'.\n");
@@ -6668,11 +6712,11 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_NODOSAGE | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "lip-subset", 11)) {
 	if (!flip_fname) {
-          logprint("Error: --flip-subset must be used with --flip.\n");
+          logerrprint("Error: --flip-subset must be used with --flip.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (allelexxxx) {
 	  // fix for this is too messy to be worthwhile
-	  logprint("Error: --flip-subset cannot be used with --allele1234/ACGT.\n");
+	  logerrprint("Error: --flip-subset cannot be used with --allele1234/ACGT.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -6684,7 +6728,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ast-epistasis", 14)) {
 	if (epi_info.modifier & EPI_REG) {
-	  logprint("Error: --fast-epistasis cannot be used with --epistasis.\n");
+	  logerrprint("Error: --fast-epistasis cannot be used with --epistasis.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 4)) {
@@ -6703,7 +6747,7 @@ int32_t main(int32_t argc, char** argv) {
 	      goto main_ret_INVALID_CMDLINE_2A;
 	    }
 	    if (epi_info.modifier & EPI_FAST_CASE_ONLY) {
-	      logprint("Error: --fast-epistasis boost does not have a case-only mode.\n");
+	      logerrprint("Error: --fast-epistasis boost does not have a case-only mode.\n");
               goto main_ret_INVALID_CMDLINE;
 	    }
 	    epi_info.modifier |= EPI_FAST_BOOST;
@@ -6715,7 +6759,7 @@ int32_t main(int32_t argc, char** argv) {
 	    epi_info.modifier |= EPI_FAST_JOINT_EFFECTS;
 	  } else if (!strcmp(argv[cur_arg + uii], "case-only")) {
 	    if (epi_info.modifier & EPI_FAST_BOOST) {
-              logprint("Error: --fast-epistasis boost does not have a case-only mode.\n");
+              logerrprint("Error: --fast-epistasis boost does not have a case-only mode.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    epi_info.modifier |= EPI_FAST_CASE_ONLY;
@@ -6751,7 +6795,7 @@ int32_t main(int32_t argc, char** argv) {
         calculation_type |= CALC_FLIPSCAN;
       } else if (!memcmp(argptr2, "lip-scan-window", 16)) {
         if (!(calculation_type & CALC_FLIPSCAN)) {
-	  logprint("Error: --flip-scan-window must be used with --flip-scan.\n");
+	  logerrprint("Error: --flip-scan-window must be used with --flip-scan.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -6763,7 +6807,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lip-scan-window-kb", 22)) {
         if (!(calculation_type & CALC_FLIPSCAN)) {
-	  logprint("Error: --flip-scan-window-kb must be used with --flip-scan.\n");
+	  logerrprint("Error: --flip-scan-window-kb must be used with --flip-scan.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -6780,7 +6824,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "lip-scan-threshold", 19)) {
         if (!(calculation_type & CALC_FLIPSCAN)) {
-	  logprint("Error: --flip-scan-threshold must be used with --flip-scan.\n");
+	  logerrprint("Error: --flip-scan-threshold must be used with --flip-scan.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -6793,14 +6837,14 @@ int32_t main(int32_t argc, char** argv) {
         ld_info.flipscan_thresh = dxx;
       } else if (!memcmp(argptr2, "lip-scan-verbose", 17)) {
 	if (!(calculation_type & CALC_FLIPSCAN)) {
-	  logprint("Error: --flip-scan-verbose must be used with --flip-scan.\n");
+	  logerrprint("Error: --flip-scan-verbose must be used with --flip-scan.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --flip-scan-verbose flag deprecated.  Use '--flip-scan verbose'.\n");
         ld_info.modifier |= LD_FLIPSCAN_VERBOSE;
       } else if (!memcmp(argptr2, "amily", 6)) {
 	if (calculation_type & CALC_DFAM) {
-	  logprint("Error: --family cannot be used with --dfam.\n");
+	  logerrprint("Error: --family cannot be used with --dfam.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         misc_flags |= MISC_FAMILY_CLUSTERS;
@@ -6808,10 +6852,10 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ill-missing-a2", 15)) {
 	if (load_rare & LOAD_RARE_CNV) {
-	  logprint("Error: --fill-missing-a2 cannot be used with a .cnv fileset.\n");
+	  logerrprint("Error: --fill-missing-a2 cannot be used with a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (flip_subset_fname) {
-	  logprint("Error: --fill-missing-a2 cannot be used with --flip-subset.\n");
+	  logerrprint("Error: --fill-missing-a2 cannot be used with --flip-subset.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	misc_flags |= MISC_FILL_MISSING_A2;
@@ -6862,13 +6906,13 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --gen parameter too long.\n");
+	  logerrprint("Error: --gen parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(pedname, argv[cur_arg + 1]);
       } else if (!memcmp(argptr2, "enome", 6)) {
 	if (genome_modifier & GENOME_OUTPUT_GZ) {
-          logprint("Warning: Duplicate --genome flag.  (--Z-genome is treated as '--genome gz'.)\n");
+          logerrprint("Warning: Duplicate --genome flag.  (--Z-genome is treated as '--genome gz'.)\n");
 	}
 	kk = 0;
       main_genome_flag:
@@ -6894,7 +6938,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_GENOME;
       } else if (!memcmp(argptr2, "enome-full", 11)) {
 	if (!(calculation_type & CALC_GENOME)) {
-	  logprint("Error: --genome-full must be used with --genome.\n");
+	  logerrprint("Error: --genome-full must be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --genome-full flag deprecated.  Use '--genome full'.\n");
@@ -6918,7 +6962,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	calculation_type |= CALC_GROUPDIST;
       } else if (!memcmp(argptr2, "rm", 3)) {
-	logprint("Error: --grm has been retired due to inconsistent meaning across GCTA versions.\nUse --grm-gz or --grm-bin.\n");
+	logerrprint("Error: --grm has been retired due to inconsistent meaning across GCTA versions.\nUse --grm-gz or --grm-bin.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "rm-gz", 6)) {
 	if (load_params || load_rare) {
@@ -6930,7 +6974,7 @@ int32_t main(int32_t argc, char** argv) {
         if (param_ct) {
 	  sptr = argv[cur_arg + 1];
 	  if (strlen(sptr) > (FNAMESIZE - 8)) {
-	    logprint("Error: --grm-gz parameter too long.\n");
+	    logerrprint("Error: --grm-gz parameter too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  strcpy(pedname, sptr);
@@ -6948,7 +6992,7 @@ int32_t main(int32_t argc, char** argv) {
         if (param_ct) {
 	  sptr = argv[cur_arg + 1];
 	  if (strlen(sptr) > (FNAMESIZE - 11)) {
-	    logprint("Error: --grm-bin parameter too long.\n");
+	    logerrprint("Error: --grm-bin parameter too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  strcpy(pedname, sptr);
@@ -6961,7 +7005,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (!covar_fname) {
-	  logprint("Error: --gxe must be used with --covar.\n");
+	  logerrprint("Error: --gxe must be used with --covar.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (param_ct) {
@@ -6975,7 +7019,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_GXE;
       } else if (!memcmp(argptr2, "enedrop", 8)) {
 	if (model_modifier & MODEL_QMASK) {
-	  logprint("Error: --assoc 'qt-means'/'lin' does not make sense with --genedrop.\n");
+	  logerrprint("Error: --assoc 'qt-means'/'lin' does not make sense with --genedrop.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --genedrop flag deprecated.  Use e.g. '--model genedrop'.\n");
@@ -6984,7 +7028,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "c", 2)) {
         if (!mtest_adjust) {
-	  logprint("Error: --gc must be used with --adjust.\n");
+	  logerrprint("Error: --gc must be used with --adjust.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --gc flag deprecated.  Use '--adjust gc'.\n");
@@ -7001,7 +7045,7 @@ int32_t main(int32_t argc, char** argv) {
 	sptr = argv[cur_arg + 1];
 	uii = strlen(sptr);
 	if (uii > (FNAMESIZE - 6)) {
-	  logprint("Error: --gfile parameter too long.\n");
+	  logerrprint("Error: --gfile parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(memcpya(pedname, sptr, uii), ".gvar", 6);
@@ -7011,17 +7055,17 @@ int32_t main(int32_t argc, char** argv) {
 	memcpy(memcpya(mapname, sptr, uii), ".map", 5);
 	load_rare = LOAD_RARE_GVAR;
       } else if (!memcmp(argptr2, "enome-lists", 12)) {
-	logprint("Error: --genome-lists flag retired.  Use --parallel.\n");
+	logerrprint("Error: --genome-lists flag retired.  Use --parallel.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "enome-minimal", 14)) {
-	logprint("Error: --genome-minimal flag retired.  Use '--genome gz'.\n");
+	logerrprint("Error: --genome-minimal flag retired.  Use '--genome gz'.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if ((!memcmp(argptr2, "roup-avg", 9)) || (!memcmp(argptr2, "roup-average", 13))) {
         if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --group-avg must be used with --cluster.\n");
+	  logerrprint("Error: --group-avg must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (cluster.modifier & CLUSTER_OLD_TIEBREAKS) {
-	  logprint("Error: --cluster 'group-avg' and 'old-tiebreaks' cannot be used together.\n");
+	  logerrprint("Error: --cluster 'group-avg' and 'old-tiebreaks' cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         LOGPRINTF("Note: --%s flag deprecated.  Use '--cluster group-avg'.\n", argptr);
@@ -7029,7 +7073,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "enotypic", 9)) {
 	if (glm_modifier & GLM_DOMINANT) {
-	  logprint("Error: --genotypic cannot be used with --dominant.\n");
+	  logerrprint("Error: --genotypic cannot be used with --dominant.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --genotypic flag deprecated.  Use e.g. '--linear genotypic'.\n");
@@ -7047,7 +7091,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_NODOSAGE | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "ene-all", 8)) {
 	if (set_info.genekeep_flattened) {
-          logprint("Error: --gene-all cannot be used with --gene.\n");
+          logerrprint("Error: --gene-all cannot be used with --gene.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
         set_info.modifier |= SET_GENE_ALL;
@@ -7055,7 +7099,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ap", 3)) {
 	if ((epi_info.modifier & (EPI_FAST | EPI_FAST_CASE_ONLY)) != (EPI_FAST | EPI_FAST_CASE_ONLY)) {
-	  logprint("Error: --gap must be used with '--fast-epistasis case-only'.\n");
+	  logerrprint("Error: --gap must be used with '--fast-epistasis case-only'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7109,7 +7153,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ene-subset", 11)) {
 	if (!gene_report_fname) {
-	  logprint("Error: --gene-subset must be used with --gene-report.\n");
+	  logerrprint("Error: --gene-subset must be used with --gene-report.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7121,10 +7165,10 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ene-report-snp-field", 21)) {
 	if (!extractname) {
-	  logprint("Error: --gene-report-snp-field must be used with --extract.\n");
+	  logerrprint("Error: --gene-report-snp-field must be used with --extract.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (misc_flags & MISC_EXTRACT_RANGE) {
-	  logprint("Error: --gene-report-snp-field cannot be used with '--extract range'.\n");
+	  logerrprint("Error: --gene-report-snp-field cannot be used with '--extract range'.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7136,7 +7180,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "plink", 6)) {
         misc_flags |= MISC_GPLINK;
       } else if (!memcmp(argptr2, "ates", 5)) {
-        logprint("Error: --gates is not implemented yet.\n");
+        logerrprint("Error: --gates is not implemented yet.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
         goto main_ret_1;
       } else {
@@ -7160,7 +7204,7 @@ int32_t main(int32_t argc, char** argv) {
 	    hwe_modifier |= HWE_THRESH_ALL;
 	  } else {
 	    if (scan_double(argv[cur_arg + uii], &hwe_thresh) || ujj) {
-	      logprint("Error: Invalid --hwe parameter sequence.\n");
+	      logerrprint("Error: Invalid --hwe parameter sequence.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
             ujj = 1;
@@ -7171,11 +7215,11 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	}
 	if (!ujj) {
-	  logprint("Error: --hwe now requires a p-value threshold.\n");
+	  logerrprint("Error: --hwe now requires a p-value threshold.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if ((hwe_modifier & HWE_THRESH_MIDP) && (hwe_thresh >= 0.5)) {
-	  logprint("Error: --hwe threshold must be smaller than 0.5 when using mid-p adjustment.\n");
+	  logerrprint("Error: --hwe threshold must be smaller than 0.5 when using mid-p adjustment.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	filter_flags |= FILTER_ALL_REQ | FILTER_NODOSAGE | FILTER_NOCNV;
@@ -7223,13 +7267,13 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "group")) {
 	    if (homozyg.modifier & HOMOZYG_GROUP_VERBOSE) {
-	      logprint("Error: --homozyg 'group' and 'group-verbose' modifiers cannot be used together.\n");
+	      logerrprint("Error: --homozyg 'group' and 'group-verbose' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    homozyg.modifier |= HOMOZYG_GROUP;
 	  } else if (!strcmp(argv[cur_arg + uii], "group-verbose")) {
 	    if (homozyg.modifier & HOMOZYG_GROUP) {
-	      logprint("Error: --homozyg 'group' and 'group-verbose' modifiers cannot be used together.\n");
+	      logerrprint("Error: --homozyg 'group' and 'group-verbose' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    homozyg.modifier |= HOMOZYG_GROUP_VERBOSE;
@@ -7294,7 +7338,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (homozyg.max_hets && (homozyg.modifier & HOMOZYG_EXTEND)) {
-	  logprint("Error: --homozyg-het with a nonzero parameter cannot be used with --homozyg\nextend.  For fine-grained control over heterozygote frequency, use\n--homozyg-window-snp and --homozyg-window-het instead.\n");
+	  logerrprint("Error: --homozyg-het with a nonzero parameter cannot be used with --homozyg\nextend.  For fine-grained control over heterozygote frequency, use\n--homozyg-window-snp and --homozyg-window-het instead.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	calculation_type |= CALC_HOMOZYG;
@@ -7308,7 +7352,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
         calculation_type |= CALC_HOMOZYG;
       } else if (!memcmp(argptr2, "omozyg-window-kb", 17)) {
-        logprint("Error: --homozyg-window-kb flag provisionally retired, since it had no effect\nin PLINK 1.07.\n");
+        logerrprint("Error: --homozyg-window-kb flag provisionally retired, since it had no effect\nin PLINK 1.07.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "omozyg-window-het", 18)) {
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7360,18 +7404,18 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "omozyg-verbose", 15)) {
 	if (!(homozyg.modifier & HOMOZYG_GROUP)) {
-	  logprint("Error: --homozyg-verbose must be used with --homozyg group.\n");
+	  logerrprint("Error: --homozyg-verbose must be used with --homozyg group.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --homozyg-verbose flag deprecated.  Use '--homozyg group-verbose'.\n");
 	homozyg.modifier = (homozyg.modifier & (~HOMOZYG_GROUP)) | HOMOZYG_GROUP_VERBOSE;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "omozyg-include-missing", 23)) {
-        logprint("Error: --homozyg-include-missing flag provisionally retired, since it had no\neffect in PLINK 1.07.\n");
+        logerrprint("Error: --homozyg-include-missing flag provisionally retired, since it had no\neffect in PLINK 1.07.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ethom", 6)) {
 	if (!(glm_modifier & GLM_GENOTYPIC)) {
-	  logprint("Error: --hethom must be used with --genotypic.\n");
+	  logerrprint("Error: --hethom must be used with --genotypic.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --hethom flag deprecated.  Use e.g. '--linear hethom' (and\n'--condition-list [filename] recessive' to change covariate coding).\n");
@@ -7385,7 +7429,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ard-call-threshold", 19)) {
         if (!(load_params & (LOAD_PARAMS_OX_ALL))) {
-	  logprint("Error: --hard-call-threshold must be used with --data, --gen, or --bgen.\n");
+	  logerrprint("Error: --hard-call-threshold must be used with --data, --gen, or --bgen.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7401,7 +7445,7 @@ int32_t main(int32_t argc, char** argv) {
 	    sprintf(logbuf, "Error: The --hard-call-threshold parameter must be smaller than 0.5.  (Did you\nmean '--hard-call-threshold %g'?)\n", 1.0 - dxx);
 	    goto main_ret_INVALID_CMDLINE_2A; 
 	  } else if (dxx > (0.5 - SMALLISH_EPSILON)) {
-	    logprint("Error: The --hard-call-threshold parameter must be smaller than 0.5, to prevent\nties.\n");
+	    logerrprint("Error: The --hard-call-threshold parameter must be smaller than 0.5, to prevent\nties.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  hard_call_threshold = dxx * (1 + SMALL_EPSILON);
@@ -7430,10 +7474,10 @@ int32_t main(int32_t argc, char** argv) {
 		 (!memcmp(argptr2, "ap-window", 10)) ||
                  (!memcmp(argptr2, "omozyg-haplo-track", 19))) {
       main_hap_disabled_message:
-        logprint("Error: The --hap... family of flags has not been reimplemented in PLINK 1.9 due\nto poor phasing accuracy (and, consequently, inferior haplotype\nlikelihood/frequency estimates) relative to other software; for now, we\nrecommend using BEAGLE instead of PLINK for case/control haplotype association\nanalysis.  (You can use '--recode beagle' to export data.)  We apologize for\nthe inconvenience, and plan to develop variants of the --hap... flags which\nhandle pre-phased da [...]
+        logerrprint("Error: The --hap... family of flags has not been reimplemented in PLINK 1.9 due\nto poor phasing accuracy (and, consequently, inferior haplotype\nlikelihood/frequency estimates) relative to other software; for now, we\nrecommend using BEAGLE instead of PLINK for case/control haplotype association\nanalysis.  (You can use '--recode beagle' to export data.)  We apologize for\nthe inconvenience, and plan to develop variants of the --hap... flags which\nhandle pre-phased [...]
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ard-call", 9)) {
-	logprint("Error: The undocumented --hard-call flag has been retired.  (The\n--hard-call-threshold flag, supported by both PLINK and PLINK/SEQ, has similar\nfunctionality.)\n");
+	logerrprint("Error: The undocumented --hard-call flag has been retired.  (The\n--hard-call-threshold flag, supported by both PLINK and PLINK/SEQ, has similar\nfunctionality.)\n");
 	goto main_ret_INVALID_CMDLINE;
       } else {
 	goto main_ret_INVALID_CMDLINE_UNRECOGNIZED;
@@ -7446,7 +7490,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if ((!memcmp(argptr2, "ndep-pairwise", 14)) || (!memcmp(argptr2, "ndep-pairphase", 15))) {
 	if (calculation_type & CALC_LD_PRUNE) {
-	  logprint("Error: Conflicting --indep... commands.\n");
+	  logerrprint("Error: Conflicting --indep... commands.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 3, 4)) {
@@ -7493,7 +7537,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	if (param_ct == 4) {
 	  if (!match_upper(argv[cur_arg + 2], "KB")) {
-	    logprint("Error: Invalid --indep parameter sequence.\n");
+	    logerrprint("Error: Invalid --indep parameter sequence.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  ld_info.modifier |= LD_PRUNE_KB_WINDOW;
@@ -7566,7 +7610,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "bm", 3)) {
         if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --ibm must be used with --cluster.\n");
+	  logerrprint("Error: --ibm must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7577,12 +7621,12 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((dxx <= 0.0) || (dxx > 1.0)) {
-	  logprint("Error: --ibm threshold must be in (0, 1].\n");
+	  logerrprint("Error: --ibm threshold must be in (0, 1].\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         cluster.min_ibm = dxx;
       } else if (!memcmp(argptr2, "mpossible", 10)) {
-	logprint("Error: --impossible flag retired.  Use '--genome nudge', or explicitly validate\nZ0/Z1/Z2/PI_HAT in your script.\n");
+	logerrprint("Error: --impossible flag retired.  Use '--genome nudge', or explicitly validate\nZ0/Z1/Z2/PI_HAT in your script.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "nteraction", 11)) {
 	logprint("Note: --interaction flag deprecated.  Use e.g. '--linear interaction'.\n");
@@ -7598,10 +7642,10 @@ int32_t main(int32_t argc, char** argv) {
         if (param_ct) {
           id_delim = extract_char_param(argv[cur_arg + 1]);
 	  if (!id_delim) {
-	    logprint("Error: --id-delim parameter must be a single character.\n");
+	    logerrprint("Error: --id-delim parameter must be a single character.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if (((unsigned char)id_delim) < ' ') {
-	    logprint("Error: --id-delim parameter cannot be tab, newline, or a nonprinting character.\n");
+	    logerrprint("Error: --id-delim parameter cannot be tab, newline, or a nonprinting character.\n");
             goto main_ret_INVALID_CMDLINE;
 	  }
 	} else {
@@ -7611,11 +7655,11 @@ int32_t main(int32_t argc, char** argv) {
         logprint("Note: --inter-chr flag deprecated.  Use e.g. '--r2 inter-chr'.\n");
 	ld_info.modifier |= LD_INTER_CHR;
       } else if (!memcmp(argptr2, "nd-major", 9)) {
-	logprint("Error: --ind-major is retired, to discourage creation of .bed files that\nconstantly have to be transposed back.  --recode exports sample-major files\nwhich are good enough for smaller jobs; we suggest transposing small data\nwindows on the fly when tackling large jobs.\n");
+	logerrprint("Error: --ind-major is retired, to discourage creation of .bed files that\nconstantly have to be transposed back.  --recode exports sample-major files\nwhich are good enough for smaller jobs; we suggest transposing small data\nwindows on the fly when tackling large jobs.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "mpute-sex", 10)) {
 	if (calculation_type & CALC_SEXCHECK) {
-	  logprint("Error: --check-sex is redundant with --impute-sex.\n");
+	  logerrprint("Error: --check-sex is redundant with --impute-sex.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 5)) {
@@ -7647,27 +7691,27 @@ int32_t main(int32_t argc, char** argv) {
 		}
 	      } else if (ujj == 2) {
 		if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_f_yobs)) {
-		  logprint("Error: Invalid --impute-sex female Ychr maximum nonmissing genotype count.\n");
+		  logerrprint("Error: Invalid --impute-sex female Ychr maximum nonmissing genotype count.\n");
 		  goto main_ret_INVALID_CMDLINE_A;
 		}
 	      } else if (ujj == 3) {
 		if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_m_yobs)) {
-		  logprint("Error: Invalid --impute-sex male Ychr minimum nonmissing genotype count.\n");
+		  logerrprint("Error: Invalid --impute-sex male Ychr minimum nonmissing genotype count.\n");
 		  goto main_ret_INVALID_CMDLINE_A;
 		}
 	      } else {
-		logprint("Error: Invalid --impute-sex parameter sequence.\n");
+		logerrprint("Error: Invalid --impute-sex parameter sequence.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	      ujj++;
 	    }
 	  }
 	  if ((ujj > 2) && (!(misc_flags & MISC_SEXCHECK_YCOUNT))) {
-	    logprint("Error: --impute-sex only accepts >2 numeric parameters in 'ycount' mode.\n");
+	    logerrprint("Error: --impute-sex only accepts >2 numeric parameters in 'ycount' mode.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  if (check_sex_fthresh > check_sex_mthresh) {
-	    logprint("Error: --impute-sex female F estimate ceiling cannot be larger than male floor.\n");
+	    logerrprint("Error: --impute-sex female F estimate ceiling cannot be larger than male floor.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	} else {
@@ -7678,27 +7722,27 @@ int32_t main(int32_t argc, char** argv) {
 	      continue;
 	    }
 	    if (!strcmp(argv[cur_arg + uii], "ycount")) {
-	      logprint("Error: Conflicting --impute-sex modes.\n");
+	      logerrprint("Error: Conflicting --impute-sex modes.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (!ukk) {
 	      if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_f_yobs)) {
-		logprint("Error: Invalid --impute-sex y-only female Ychr maximum nonmissing genotype\ncount.\n");
+		logerrprint("Error: Invalid --impute-sex y-only female Ychr maximum nonmissing genotype\ncount.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	    } else if (ukk == 1) {
 	      if (scan_uint_defcap(argv[cur_arg + uii], &check_sex_m_yobs)) {
-		logprint("Error: Invalid --impute-sex y-only male Ychr minimum nonmissing genotype count.\n");
+		logerrprint("Error: Invalid --impute-sex y-only male Ychr minimum nonmissing genotype count.\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	    } else {
-	      logprint("Error: Invalid --impute-sex y-only parameter sequence.\n");
+	      logerrprint("Error: Invalid --impute-sex y-only parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    ukk++;
 	  }
 	  if (check_sex_f_yobs >= check_sex_m_yobs) {
-	    logprint("Error: In y-only mode, --impute-sex female Y observation threshold must be\nsmaller than male threshold.\n");
+	    logerrprint("Error: In y-only mode, --impute-sex female Y observation threshold must be\nsmaller than male threshold.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  misc_flags |= MISC_SEXCHECK_YCOUNT | MISC_SEXCHECK_YONLY;
@@ -7707,7 +7751,7 @@ int32_t main(int32_t argc, char** argv) {
         misc_flags |= MISC_IMPUTE_SEX;
 	sex_missing_pheno |= ALLOW_NO_SEX;
       } else if ((!memcmp(argptr2, "d-dict", 7)) || (!memcmp(argptr2, "d-dump", 7)) || (!memcmp(argptr2, "d-lookup", 9)) || (!memcmp(argptr2, "d-match", 8)) || (!memcmp(argptr2, "d-replace", 10)) || (!memcmp(argptr2, "d-table", 8))) {
-	logprint("Error: --id-dict and --id-match are provisionally retired, since free database\nsoftware handles these operations in a more flexible and powerful manner.\nContact the developers if you still need them.\n");
+	logerrprint("Error: --id-dict and --id-match are provisionally retired, since free database\nsoftware handles these operations in a more flexible and powerful manner.\nContact the developers if you still need them.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else {
 	goto main_ret_INVALID_CMDLINE_UNRECOGNIZED;
@@ -7717,7 +7761,7 @@ int32_t main(int32_t argc, char** argv) {
     case 'j':
       if (!memcmp(argptr2, "e-cellmin", 10)) {
         if (!(epi_info.modifier & EPI_FAST_JOINT_EFFECTS)) {
-	  logprint("Error: --je-cellmin must be used with '--fast-epistasis joint-effects'.\n");
+	  logerrprint("Error: --je-cellmin must be used with '--fast-epistasis joint-effects'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -7802,7 +7846,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	if (param_ct) {
 	  if (strlen(argv[cur_arg + 1]) > FNAMESIZE - 6) {
-	    logprint("Error: --lfile filename prefix too long.\n");
+	    logerrprint("Error: --lfile filename prefix too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  strcpy(pedname, argv[cur_arg + 1]);
@@ -7812,13 +7856,13 @@ int32_t main(int32_t argc, char** argv) {
 	load_rare = LOAD_RARE_LGEN;
       } else if (!memcmp(argptr2, "oop-assoc", 10)) {
 	if (pheno_modifier & PHENO_ALL) {
-	  logprint("Error: --loop-assoc cannot be used with --all-pheno.\n");
+	  logerrprint("Error: --loop-assoc cannot be used with --all-pheno.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (misc_flags & MISC_FAMILY_CLUSTERS) {
-	  logprint("Error: --loop-assoc cannot be used with --family.\n");
+	  logerrprint("Error: --loop-assoc cannot be used with --family.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (load_rare == LOAD_RARE_DOSAGE) {
-	  logprint("Error: --loop-assoc cannot be used with --dosage.\n");
+	  logerrprint("Error: --loop-assoc cannot be used with --dosage.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -7829,7 +7873,7 @@ int32_t main(int32_t argc, char** argv) {
 	  if ((strlen(argv[cur_arg + 1]) == 7) && (!memcmp(argv[cur_arg + 1], "keep-", 5)) && match_upper(&(argv[cur_arg + 1][5]), "NA")) {
 	    uii = 2;
 	  } else if ((strlen(argv[cur_arg + 2]) != 7) || memcmp(argv[cur_arg + 2], "keep-", 5) || (!match_upper(&(argv[cur_arg + 2][5]), "NA"))) {
-            logprint("Error: Invalid --loop-assoc parameter sequence.\n");
+            logerrprint("Error: Invalid --loop-assoc parameter sequence.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
           misc_flags |= MISC_LOAD_CLUSTER_KEEP_NA;
@@ -7840,7 +7884,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "og10", 5)) {
         if (!mtest_adjust) {
-	  logprint("Error: --log10 must be used with --adjust.\n");
+	  logerrprint("Error: --log10 must be used with --adjust.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --log10 flag deprecated.  Use '--adjust log10'.\n");
@@ -7865,7 +7909,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if ((!memcmp(argptr2, "inear", 6)) || (!memcmp(argptr2, "ogistic", 8))) {
 #ifndef NOLAPACK
         if (calculation_type & CALC_GLM) {
-	  logprint("Error: --logistic cannot be used with --linear.\n");
+	  logerrprint("Error: --logistic cannot be used with --linear.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 #endif
@@ -7873,7 +7917,7 @@ int32_t main(int32_t argc, char** argv) {
 	  glm_modifier |= GLM_LOGISTIC;
 #ifdef NOLAPACK
 	} else {
-	  logprint("Error: --linear requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
+	  logerrprint("Error: --linear requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
 	  goto main_ret_INVALID_CMDLINE;
 #endif
 	}
@@ -7881,7 +7925,7 @@ int32_t main(int32_t argc, char** argv) {
 	  // make --dosage + modifier-free --linear/--logistic only issue a
 	  // warning
 	  if (param_ct) {
-	    logprint("Error: --dosage cannot be used with --linear/--logistic modifiers.\n");
+	    logerrprint("Error: --dosage cannot be used with --linear/--logistic modifiers.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  logprint("Note: --dosage automatically performs a regression; --linear/--logistic has no\nadditional effect.\n");
@@ -7966,13 +8010,13 @@ int32_t main(int32_t argc, char** argv) {
 	      glm_modifier |= GLM_INTERACTION;
 	    } else if (!strcmp(argv[cur_arg + uii], "standard-beta")) {
 	      if (glm_modifier & GLM_LOGISTIC) {
-		logprint("Error: --logistic does not have a 'standard-beta' modifier.  (Did you mean\n--linear or 'beta'?)\n");
+		logerrprint("Error: --logistic does not have a 'standard-beta' modifier.  (Did you mean\n--linear or 'beta'?)\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	      glm_modifier |= GLM_STANDARD_BETA;
 	    } else if (!strcmp(argv[cur_arg + uii], "intercept")) {
 	      if (glm_modifier & GLM_LOGISTIC) {
-		logprint("Error: --logistic does not currently have a 'intercept' modifier.  (Did you\nmean --linear or 'beta'?)\n");
+		logerrprint("Error: --logistic does not currently have a 'intercept' modifier.  (Did you\nmean --linear or 'beta'?)\n");
 		goto main_ret_INVALID_CMDLINE_A;
 	      }
 	      glm_modifier |= GLM_INTERCEPT;
@@ -8020,7 +8064,7 @@ int32_t main(int32_t argc, char** argv) {
 	  if (!strcmp(argv[cur_arg + uii], "report-zeroes")) {
 	    misc_flags |= MISC_LASSO_REPORT_ZEROES;
 	  } else if (lasso_minlambda > 0) {
-            logprint("Error: Invalid --lasso parameter sequence.\n");
+            logerrprint("Error: Invalid --lasso parameter sequence.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  } else if (scan_double(argv[cur_arg + uii], &lasso_minlambda) || (lasso_minlambda <= 0)) {
 	    sprintf(logbuf, "Error: Invalid --lasso minimum lambda '%s'.\n", argv[cur_arg + uii]);
@@ -8030,10 +8074,10 @@ int32_t main(int32_t argc, char** argv) {
         calculation_type |= CALC_LASSO;
       } else if (!memcmp(argptr2, "asso-select-covars", 19)) {
 	if (!(calculation_type & CALC_LASSO)) {
-	  logprint("Error: --lasso-select-covars must be used with --lasso.\n");
+	  logerrprint("Error: --lasso-select-covars must be used with --lasso.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (!covar_fname) {
-	  logprint("Error: --lasso-select-covars must be used with --covar.\n");
+	  logerrprint("Error: --lasso-select-covars must be used with --covar.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         misc_flags |= MISC_LASSO_SELECT_COVARS;
@@ -8136,7 +8180,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	calculation_type |= CALC_DUPVAR;
       } else if (!memcmp(argptr2, "d-pred", 7)) {
-	logprint("Error: --ld-pred is currently under development.\n");
+	logerrprint("Error: --ld-pred is currently under development.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto main_ret_1;
       } else if ((!memcmp(argptr2, "ookup", 6)) ||
@@ -8144,10 +8188,10 @@ int32_t main(int32_t argc, char** argv) {
                  (!memcmp(argptr2, "ookup-gene", 11)) ||
                  (!memcmp(argptr2, "ookup-gene-kb", 14)) ||
                  (!memcmp(argptr2, "ookup-gene-list", 16))) {
-        logprint("Error: --lookup commands have been retired since the Sullivan Lab web database\nis no longer operational.  Use e.g. PLINK/SEQ's lookup command instead.\n");
+        logerrprint("Error: --lookup commands have been retired since the Sullivan Lab web database\nis no longer operational.  Use e.g. PLINK/SEQ's lookup command instead.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "iability", 9)) {
-	logprint("Error: --liability is provisionally retired.  Contact the developers if you\nneed this option.\n");
+	logerrprint("Error: --liability is provisionally retired.  Contact the developers if you\nneed this option.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else {
         goto main_ret_INVALID_CMDLINE_UNRECOGNIZED;
@@ -8164,7 +8208,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --map parameter too long.\n");
+	  logerrprint("Error: --map parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(mapname, argv[cur_arg + 1]);
@@ -8225,7 +8269,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "filter", 7)) {
 	if (!filtername) {
-	  logprint("Error: --mfilter must be used with --filter.\n");
+	  logerrprint("Error: --mfilter must be used with --filter.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8250,7 +8294,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 #ifndef __LP64__
 	if (malloc_size_mb > 2047) {
-	  logprint("Error: --memory parameter too large for 32-bit version (max 2047).\n");
+	  logerrprint("Error: --memory parameter too large for 32-bit version (max 2047).\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 #endif
@@ -8308,15 +8352,15 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	filter_flags |= FILTER_ALL_REQ | FILTER_NODOSAGE | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "ake-grm", 8)) {
-	logprint("Error: --make-grm has been retired due to inconsistent meaning across GCTA\nversions.  Use --make-grm-gz or --make-grm-bin.\n");
+	logerrprint("Error: --make-grm has been retired due to inconsistent meaning across GCTA\nversions.  Use --make-grm-gz or --make-grm-bin.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ake-grm-gz", 11)) {
 	if (calculation_type & CALC_RELATIONSHIP) {
-	  logprint("Error: --make-grm-bin cannot be used with --make-grm-gz.\n");
+	  logerrprint("Error: --make-grm-bin cannot be used with --make-grm-gz.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (distance_exp != 0.0) {
-	  logprint("Error: '--distance-wts exp=[x]' cannot be used with --make-grm-gz.\n");
+	  logerrprint("Error: '--distance-wts exp=[x]' cannot be used with --make-grm-gz.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 2)) {
@@ -8326,11 +8370,11 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "cov")) {
 	    if (calculation_type & CALC_IBC) {
-	      logprint("Error: --make-grm-gz 'cov' modifier cannot coexist with --ibc flag.\n");
+	      logerrprint("Error: --make-grm-gz 'cov' modifier cannot coexist with --ibc flag.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (rel_info.ibc_type) {
-	      logprint("Error: --make-grm-gz 'cov' modifier cannot coexist with an IBC modifier.\n");
+	      logerrprint("Error: --make-grm-gz 'cov' modifier cannot coexist with an IBC modifier.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_COV;
@@ -8338,7 +8382,7 @@ int32_t main(int32_t argc, char** argv) {
 	    rel_info.modifier &= ~REL_CALC_GZ;
 	  } else if ((!strcmp(argv[cur_arg + uii], "ibc2")) || (!strcmp(argv[cur_arg + uii], "ibc3"))) {
 	    if (rel_info.modifier & REL_CALC_COV) {
-	      logprint("Error: --make-grm-gz 'cov' modifier cannot coexist with an IBC modifier.\n");
+	      logerrprint("Error: --make-grm-gz 'cov' modifier cannot coexist with an IBC modifier.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (rel_info.ibc_type) {
@@ -8347,7 +8391,7 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	    rel_info.ibc_type = argv[cur_arg + uii][3] - '0';
 	  } else if (!strcmp(argv[cur_arg + uii], "single-prec")) {
-	    logprint("Error: --make-grm-gz 'single-prec' modifier has been retired.\n");
+	    logerrprint("Error: --make-grm-gz 'single-prec' modifier has been retired.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else {
 	    sprintf(logbuf, "Error: Invalid --make-grm-gz parameter '%s'.\n", argv[cur_arg + uii]);
@@ -8357,7 +8401,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_RELATIONSHIP;
       } else if (!memcmp(argptr2, "ake-grm-bin", 12)) {
 	if (distance_exp != 0.0) {
-	  logprint("Error: '--distance-wts exp=[x]' cannot be used with --make-grm-bin.\n");
+	  logerrprint("Error: '--distance-wts exp=[x]' cannot be used with --make-grm-bin.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -8367,7 +8411,7 @@ int32_t main(int32_t argc, char** argv) {
 	if (param_ct) {
 	  if (!strcmp(argv[cur_arg + 1], "cov")) {
 	    if (calculation_type & CALC_IBC) {
-	      logprint("Error: --make-grm-bin 'cov' modifier cannot coexist with --ibc flag.\n");
+	      logerrprint("Error: --make-grm-bin 'cov' modifier cannot coexist with --ibc flag.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_COV;
@@ -8381,11 +8425,11 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_RELATIONSHIP;
       } else if (!memcmp(argptr2, "ake-rel", 8)) {
 	if (calculation_type & CALC_RELATIONSHIP) {
-	  logprint("Error: --make-rel cannot be used with --make-grm-gz/--make-grm-bin.\n");
+	  logerrprint("Error: --make-rel cannot be used with --make-grm-gz/--make-grm-bin.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (distance_exp != 0.0) {
-	  logprint("Error: '--distance-wts exp=[x]' cannot be used with --make-rel.\n");
+	  logerrprint("Error: '--distance-wts exp=[x]' cannot be used with --make-rel.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 3)) {
@@ -8394,62 +8438,62 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "cov")) {
 	    if (calculation_type & CALC_IBC) {
-	      logprint("Error: --make-rel 'cov' modifier cannot coexist with --ibc flag.\n");
+	      logerrprint("Error: --make-rel 'cov' modifier cannot coexist with --ibc flag.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (rel_info.ibc_type) {
-	      logprint("Error: --make-rel 'cov' modifier cannot coexist with an IBC modifier.\n");
+	      logerrprint("Error: --make-rel 'cov' modifier cannot coexist with an IBC modifier.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_COV;
 	  } else if (!strcmp(argv[cur_arg + uii], "gz")) {
 	    if (rel_info.modifier & (REL_CALC_BIN | REL_CALC_BIN4)) {
-	      logprint("Error: Conflicting --make-rel modifiers.\n");
+	      logerrprint("Error: Conflicting --make-rel modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_GZ;
 	  } else if (!strcmp(argv[cur_arg + uii], "bin")) {
 	    if (rel_info.modifier & (REL_CALC_GZ | REL_CALC_BIN4)) {
-	      logprint("Error: Conflicting --make-rel modifiers.\n");
+	      logerrprint("Error: Conflicting --make-rel modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_BIN;
 	  } else if (!strcmp(argv[cur_arg + uii], "bin4")) {
 	    if (rel_info.modifier & (REL_CALC_GZ | REL_CALC_BIN)) {
-	      logprint("Error: Conflicting --make-rel modifiers.\n");
+	      logerrprint("Error: Conflicting --make-rel modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_BIN4;
 	  } else if (!strcmp(argv[cur_arg + uii], "square")) {
 	    if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_SQ0) {
-	      logprint("Error: --make-rel 'square' and 'square0' modifiers cannot coexist.\n");
+	      logerrprint("Error: --make-rel 'square' and 'square0' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_TRI) {
-	      logprint("Error: --make-rel 'square' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --make-rel 'square' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_SQ;
 	  } else if (!strcmp(argv[cur_arg + uii], "square0")) {
 	    if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_SQ) {
-	      logprint("Error: --make-rel 'square' and 'square0' modifiers cannot coexist.\n");
+	      logerrprint("Error: --make-rel 'square' and 'square0' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_TRI) {
-	      logprint("Error: --make-rel 'square0' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --make-rel 'square0' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_SQ0;
 	  } else if (!strcmp(argv[cur_arg + uii], "triangle")) {
 	    if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_SQ) {
-	      logprint("Error: --make-rel 'square' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --make-rel 'square' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_SQ0) {
-	      logprint("Error: --make-rel 'square0' and 'triangle' modifiers cannot coexist.\n");
+	      logerrprint("Error: --make-rel 'square0' and 'triangle' modifiers cannot coexist.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    rel_info.modifier |= REL_CALC_TRI;
 	  } else if ((!strcmp(argv[cur_arg + uii], "ibc2")) || (!strcmp(argv[cur_arg + uii], "ibc3"))) {
 	    if (rel_info.modifier & REL_CALC_COV) {
-	      logprint("Error: --make-rel 'cov' modifier cannot coexist with an IBC modifier.\n");
+	      logerrprint("Error: --make-rel 'cov' modifier cannot coexist with an IBC modifier.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (rel_info.ibc_type) {
@@ -8458,7 +8502,7 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	    rel_info.ibc_type = argv[cur_arg + uii][3] - '0';
 	  } else if (!strcmp(argv[cur_arg + uii], "single-prec")) {
-	    logprint("Error: --make-rel 'single-prec' modifier has been retired.  Use 'bin4'.\n");
+	    logerrprint("Error: --make-rel 'single-prec' modifier has been retired.  Use 'bin4'.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else {
 	    sprintf(logbuf, "Error: Invalid --make-rel parameter '%s'.\n", argv[cur_arg + uii]);
@@ -8478,7 +8522,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "af-succ", 8)) {
 	if (misc_flags & MISC_HET_SMALL_SAMPLE) {
-	  logprint("Error: '--het small-sample' cannot be used with --maf-succ.\n");
+	  logerrprint("Error: '--het small-sample' cannot be used with --maf-succ.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	misc_flags |= MISC_MAF_SUCC;
@@ -8488,7 +8532,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ake-bed", 8)) {
         if (misc_flags & MISC_KEEP_AUTOCONV) {
-	  logprint("Error: --make-bed cannot be used with --keep-autoconv.\n");
+	  logerrprint("Error: --make-bed cannot be used with --keep-autoconv.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
@@ -8504,7 +8548,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_MAKE_BED;
       } else if (!memcmp(argptr2, "ake-just-bim", 13)) {
 	if (calculation_type & CALC_MAKE_BED) {
-	  logprint("Error: --make-just-bim cannot be used with --make-bed.\n");
+	  logerrprint("Error: --make-just-bim cannot be used with --make-bed.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
@@ -8515,7 +8559,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ake-just-fam", 13)) {
 	if (calculation_type & CALC_MAKE_BED) {
-	  logprint("Error: --make-just-fam cannot be used with --make-bed.\n");
+	  logerrprint("Error: --make-just-fam cannot be used with --make-bed.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
@@ -8526,7 +8570,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "erge", 5)) {
 	if (calculation_type & CALC_MERGE) {
-	  logprint("Error: --merge cannot be used with --bmerge.\n");
+	  logerrprint("Error: --merge cannot be used with --bmerge.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
 	  sprintf(logbuf, "Error: --merge cannot be used with %s.\n", (load_rare == LOAD_RARE_CNV)? ".cnv filesets" : "--dosage");
@@ -8538,19 +8582,19 @@ int32_t main(int32_t argc, char** argv) {
 	jj = strlen(argv[cur_arg + 1]);
 	if (param_ct == 2) {
 	  if (++jj > FNAMESIZE) {
-	    logprint("Error: --merge .ped filename too long.\n");
+	    logerrprint("Error: --merge .ped filename too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(mergename1, argv[cur_arg + 1], jj);
 	  jj = strlen(argv[cur_arg + 2]) + 1;
 	  if (jj > FNAMESIZE) {
-	    logprint("Error: --merge .map filename too long.\n");
+	    logerrprint("Error: --merge .map filename too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(mergename2, argv[cur_arg + 2], jj);
 	} else {
 	  if (jj > (FNAMESIZE - 5)) {
-	    logprint("Error: --merge filename prefix too long.\n");
+	    logerrprint("Error: --merge filename prefix too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(memcpya(mergename1, argv[cur_arg + 1], jj), ".ped", 5);
@@ -8559,7 +8603,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_MERGE;
       } else if (!memcmp(argptr2, "erge-list", 10)) {
 	if (calculation_type & CALC_MERGE) {
-	  logprint("Error: --merge-list cannot be used with --merge or --bmerge.\n");
+	  logerrprint("Error: --merge-list cannot be used with --merge or --bmerge.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
 	  sprintf(logbuf, "Error: --merge-list cannot be used with %s.\n", (load_rare == LOAD_RARE_CNV)? ".cnv filesets" : "--dosage");
@@ -8570,7 +8614,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	jj = strlen(argv[cur_arg + 1]) + 1;
 	if (jj > FNAMESIZE) {
-	  logprint("Error: --merge-list filename too long.\n");
+	  logerrprint("Error: --merge-list filename too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(mergename1, argv[cur_arg + 1], jj);
@@ -8578,7 +8622,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_MERGE;
       } else if (!memcmp(argptr2, "erge-mode", 10)) {
 	if (!(calculation_type & CALC_MERGE)) {
-	  logprint("Error: --merge-mode must be used with --{b}merge/--merge-list.\n");
+	  logerrprint("Error: --merge-mode must be used with --{b}merge/--merge-list.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8590,7 +8634,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((merge_type & MERGE_LIST) && (cc > '5')) {
-	  logprint("Error: --merge-mode 6-7 cannot be used with --merge-list.\n");
+	  logerrprint("Error: --merge-mode 6-7 cannot be used with --merge-list.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         merge_type |= cc - '0';
@@ -8602,10 +8646,10 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "covar", 6)) {
         if (!(calculation_type & CALC_GXE)) {
-	  logprint("Error: --mcovar must be used with --covar and --gxe.\n");
+	  logerrprint("Error: --mcovar must be used with --covar and --gxe.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (gxe_mcovar > 1) {
-	  logprint("Error: --mcovar cannot be used with a --gxe parameter.\n");
+	  logerrprint("Error: --mcovar cannot be used with a --gxe parameter.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8623,25 +8667,25 @@ int32_t main(int32_t argc, char** argv) {
 	if (model_modifier & MODEL_ASSOC_FDEPR) {
 	  model_modifier &= ~(MODEL_ASSOC | MODEL_ASSOC_FDEPR);
 	} else if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --model cannot be used with --assoc.\n");
+	  logerrprint("Error: --model cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "fisher")) {
 	    if (model_modifier & MODEL_TRENDONLY) {
-	      logprint("Error: --model 'fisher' and 'trend-only' cannot be used together.\n");
+	      logerrprint("Error: --model 'fisher' and 'trend-only' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_FISHER;
 	  } else if (!strcmp(argv[cur_arg + uii], "fisher-midp")) {
 	    if (model_modifier & MODEL_TRENDONLY) {
-	      logprint("Error: --model 'fisher-midp' and 'trend-only' cannot be used together.\n");
+	      logerrprint("Error: --model 'fisher-midp' and 'trend-only' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_FISHER | MODEL_FISHER_MIDP;
 	  } else if (!strcmp(argv[cur_arg + uii], "perm")) {
 	    if (model_modifier & MODEL_MPERM) {
-	      logprint("Error: --model 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --model 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PERM;
@@ -8651,44 +8695,44 @@ int32_t main(int32_t argc, char** argv) {
 	    model_modifier |= MODEL_PERM_COUNT;
 	  } else if (!strcmp(argv[cur_arg + uii], "dom")) {
 	    if (model_modifier & (MODEL_PREC | MODEL_PGEN | MODEL_PTREND | MODEL_TRENDONLY)) {
-	      logprint("Error: Conflicting --model parameters.\n");
+	      logerrprint("Error: Conflicting --model parameters.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PDOM;
 	  } else if (!strcmp(argv[cur_arg + uii], "rec")) {
 	    if (model_modifier & (MODEL_PDOM | MODEL_PGEN | MODEL_PTREND | MODEL_TRENDONLY)) {
-	      logprint("Error: Conflicting --model parameters.\n");
+	      logerrprint("Error: Conflicting --model parameters.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PREC;
 	  } else if (!strcmp(argv[cur_arg + uii], "gen")) {
 	    if (model_modifier & (MODEL_PDOM | MODEL_PREC | MODEL_PTREND | MODEL_TRENDONLY)) {
-	      logprint("Error: Conflicting --model parameters.\n");
+	      logerrprint("Error: Conflicting --model parameters.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (mtest_adjust) {
-	      logprint("Error: --model perm-gen cannot be used with --adjust.\n");
+	      logerrprint("Error: --model perm-gen cannot be used with --adjust.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PGEN;
 	  } else if (!strcmp(argv[cur_arg + uii], "trend")) {
 	    if (model_modifier & (MODEL_PDOM | MODEL_PREC | MODEL_PGEN)) {
-	      logprint("Error: Conflicting --model parameters.\n");
+	      logerrprint("Error: Conflicting --model parameters.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PTREND;
 	  } else if (!strcmp(argv[cur_arg + uii], "trend-only")) {
 	    if (model_modifier & (MODEL_FISHER | MODEL_PDOM | MODEL_PREC | MODEL_PGEN)) {
-	      logprint("Error: Conflicting --model parameters.\n");
+	      logerrprint("Error: Conflicting --model parameters.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    model_modifier |= MODEL_PTREND | MODEL_TRENDONLY;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
 	    if (model_modifier & MODEL_PERM) {
-	      logprint("Error: --model 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --model 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (model_modifier & MODEL_MPERM) {
-	      logprint("Error: Duplicate --model 'mperm' modifier.\n");
+	      logerrprint("Error: Duplicate --model 'mperm' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &model_mperm_val)) {
@@ -8697,7 +8741,7 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	    model_modifier |= MODEL_MPERM;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-	    logprint("Error: Improper --model mperm syntax.  (Use '--model mperm=[value]'.)\n");
+	    logerrprint("Error: Improper --model mperm syntax.  (Use '--model mperm=[value]'.)\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else if (!strcmp(argv[cur_arg + uii], "set-test")) {
 	    model_modifier |= MODEL_SET_TEST;
@@ -8709,11 +8753,11 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_MODEL;
       } else if (!memcmp(argptr2, "odel-dom", 9)) {
 	if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --model-dom cannot be used with --assoc.\n");
+	  logerrprint("Error: --model-dom cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (model_modifier & (MODEL_PREC | MODEL_PGEN | MODEL_PTREND)) {
-	  logprint("Error: Conflicting --model parameters.\n");
+	  logerrprint("Error: Conflicting --model parameters.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --model-dom flag deprecated.  Use '--model dom'.\n");
@@ -8722,11 +8766,11 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "odel-gen", 9)) {
 	if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --model-gen cannot be used with --assoc.\n");
+	  logerrprint("Error: --model-gen cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (model_modifier & (MODEL_PDOM | MODEL_PREC | MODEL_PTREND)) {
-	  logprint("Error: Conflicting --model parameters.\n");
+	  logerrprint("Error: Conflicting --model parameters.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --model-gen flag deprecated.  Use '--model gen'.\n");
@@ -8735,11 +8779,11 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "odel-rec", 9)) {
 	if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --model-rec cannot be used with --assoc.\n");
+	  logerrprint("Error: --model-rec cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (model_modifier & (MODEL_PDOM | MODEL_PGEN | MODEL_PTREND)) {
-	  logprint("Error: Conflicting --model parameters.\n");
+	  logerrprint("Error: Conflicting --model parameters.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --model-rec flag deprecated.  Use '--model rec'.\n");
@@ -8748,11 +8792,11 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "odel-trend", 11)) {
 	if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --model-trend cannot be used with --assoc.\n");
+	  logerrprint("Error: --model-trend cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (model_modifier & (MODEL_PDOM | MODEL_PGEN | MODEL_PREC)) {
-	  logprint("Error: Conflicting --model parameters.\n");
+	  logerrprint("Error: Conflicting --model parameters.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --model-trend flag deprecated.  Use '--model trend'.\n");
@@ -8821,7 +8865,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "perm-save", 10)) {
 	if (glm_modifier & GLM_NO_SNP) {
-          logprint("Error: --mperm-save cannot be used with --linear/--logistic no-snp.\n");
+          logerrprint("Error: --mperm-save cannot be used with --linear/--logistic no-snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	mperm_save |= MPERM_DUMP_BEST;
@@ -8831,7 +8875,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "c", 2)) {
 	if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --mc must be used with --cluster.\n");
+	  logerrprint("Error: --mc must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8843,7 +8887,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "cc", 2)) {
 	if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --mcc must be used with --cluster.\n");
+	  logerrprint("Error: --mcc must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 2, 2)) {
@@ -8854,7 +8898,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (cluster.max_cases > cluster.max_size) {
-          logprint("Error: --mcc parameter exceeds --mc parameter.\n");
+          logerrprint("Error: --mcc parameter exceeds --mc parameter.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (scan_posint_defcap(argv[cur_arg + 2], &cluster.max_ctrls)) {
@@ -8862,12 +8906,12 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (cluster.max_ctrls > cluster.max_size) {
-          logprint("Error: --mcc parameter exceeds --mc parameter.\n");
+          logerrprint("Error: --mcc parameter exceeds --mc parameter.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
       } else if (!memcmp(argptr2, "atch", 5)) {
 	if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --match must be used with --cluster.\n");
+	  logerrprint("Error: --match must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -8884,7 +8928,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "atch-type", 10)) {
 	if (!cluster.match_fname) {
-	  logprint("Error: --match-type must be used with --match.\n");
+	  logerrprint("Error: --match-type must be used with --match.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8896,13 +8940,13 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ds-plot", 8)) {
 #ifdef NOLAPACK
-	// PLINK 1.07's SVD-based non-LAPACK implementation does not conform to
-	// classical MDS, so we do not replicate it.
-        logprint("Error: --mds-plot requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
+	// may want to support this again, now that we've switched the default
+	// algorithm back to PLINK 1.07-style SVD
+        logerrprint("Error: --mds-plot requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
 	goto main_ret_INVALID_CMDLINE;
 #else
 	if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --mds-plot must be used with --cluster.\n");
+	  logerrprint("Error: --mds-plot must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 4)) {
@@ -8918,7 +8962,7 @@ int32_t main(int32_t argc, char** argv) {
 	    cluster.modifier |= CLUSTER_MDS_EIGVALS;
 	  } else {
 	    if (cluster.mds_dim_ct) {
-	      logprint("Error: Invalid --mds-plot parameter sequence.\n");
+	      logerrprint("Error: Invalid --mds-plot parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (scan_posint_defcap(argv[cur_arg + uii], &cluster.mds_dim_ct)) {
@@ -8927,10 +8971,14 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	  }
 	}
+	if (!cluster.mds_dim_ct) {
+	  logerrprint("Error: Missing --mds-plot dimension count.\n");
+	  goto main_ret_INVALID_CMDLINE_A;
+	}
 #endif
       } else if (!memcmp(argptr2, "ds-cluster", 11)) {
 	if (!(calculation_type & CALC_CLUSTER)) {
-	  logprint("Error: --mds-cluster must be used with --cluster.\n");
+	  logerrprint("Error: --mds-cluster must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         logprint("Note: --mds-cluster flag deprecated.  Use '--mds-plot by-cluster'.\n");
@@ -8945,7 +8993,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "in", 3)) {
         if (!(calculation_type & CALC_GENOME)) {
-	  logprint("Error: --min must be used with --genome.\n");
+	  logerrprint("Error: --min must be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8956,17 +9004,17 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((dxx < -1.0) || (dxx > 1.0)) {
-          logprint("Error: --min threshold must be between -1 and 1 inclusive.\n");
+          logerrprint("Error: --min threshold must be between -1 and 1 inclusive.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (dxx > genome_max_pi_hat) {
-	  logprint("Error: --min value cannot be greater than --max value.\n");
+	  logerrprint("Error: --min value cannot be greater than --max value.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         genome_modifier |= GENOME_FILTER_PI_HAT;
 	genome_min_pi_hat = dxx;
       } else if (!memcmp(argptr2, "ax", 3)) {
         if (!(calculation_type & CALC_GENOME)) {
-	  logprint("Error: --max must be used with --genome.\n");
+	  logerrprint("Error: --max must be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -8977,7 +9025,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((dxx < -1.0) || (dxx > 1.0)) {
-          logprint("Error: --max threshold must be between -1 and 1 inclusive.\n");
+          logerrprint("Error: --max threshold must be between -1 and 1 inclusive.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	genome_modifier |= GENOME_FILTER_PI_HAT;
@@ -9011,7 +9059,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_MISSING_REPORT;
       } else if (!memcmp(argptr2, "h", 2)) {
 	if (calculation_type & CALC_CMH) {
-	  logprint("Error: --mh is redundant with --bd.\n");
+	  logerrprint("Error: --mh is redundant with --bd.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 3)) {
@@ -9020,16 +9068,16 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "perm")) {
 	    if (cluster.modifier & CLUSTER_CMH_MPERM) {
-	      logprint("Error: --mh 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --mh 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    cluster.modifier |= CLUSTER_CMH_PERM;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
 	    if (cluster.modifier & CLUSTER_CMH_PERM) {
-	      logprint("Error: --mh 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --mh 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (cluster.modifier & CLUSTER_CMH_MPERM) {
-	      logprint("Error: Duplicate --mh 'mperm' modifier.\n");
+	      logerrprint("Error: Duplicate --mh 'mperm' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &(cluster.cmh_mperm_val))) {
@@ -9042,7 +9090,7 @@ int32_t main(int32_t argc, char** argv) {
 	  } else if (!strcmp(argv[cur_arg + uii], "set-test")) {
 	    cluster.modifier |= CLUSTER_CMH_SET_TEST;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-            logprint("Error: Improper --mh mperm syntax.  (Use '--mh mperm=[value]'.)\n");
+            logerrprint("Error: Improper --mh mperm syntax.  (Use '--mh mperm=[value]'.)\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  } else {
             sprintf(logbuf, "Error: Invalid --mh parameter '%s'.\n", argv[cur_arg + uii]);
@@ -9052,7 +9100,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_CMH;
       } else if (!memcmp(argptr2, "h2", 3)) {
 	if (calculation_type & CALC_CMH) {
-	  logprint("Error: --mh2 cannot be used with --mh/--bd.\n");
+	  logerrprint("Error: --mh2 cannot be used with --mh/--bd.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	calculation_type |= CALC_CMH;
@@ -9074,7 +9122,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ;
       } else if (!memcmp(argptr2, "ake-set-border", 15)) {
 	if (!set_info.fname) {
-	  logprint("Error: --make-set-border must be used with --make-set.\n");
+	  logerrprint("Error: --make-set-border must be used with --make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9091,14 +9139,14 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ake-set-collapse-group", 23)) {
         if (!set_info.fname) {
-	  logprint("Error: --make-set-collapse-group must be used with --make-set.\n");
+	  logerrprint("Error: --make-set-collapse-group must be used with --make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         set_info.modifier |= SET_MAKE_COLLAPSE_GROUP;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ake-set-complement-all", 23)) {
 	if (set_info.modifier & SET_COMPLEMENTS) {
-	  logprint("Error: --make-set-complement-all cannot be used with --complement-sets.\n");
+	  logerrprint("Error: --make-set-complement-all cannot be used with --complement-sets.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9110,17 +9158,17 @@ int32_t main(int32_t argc, char** argv) {
 	set_info.modifier |= SET_COMPLEMENTS;
       } else if (!memcmp(argptr2, "ake-set-complement-group", 25)) {
         if (!set_info.fname) {
-	  logprint("Error: --make-set-complement-group must be used with --make-set.\n");
+	  logerrprint("Error: --make-set-complement-group must be used with --make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (set_info.modifier & (SET_COMPLEMENTS | SET_MAKE_COLLAPSE_GROUP)) {
-	  logprint("Error: --make-set-complement-group cannot be used with --complement-sets,\n--make-set-collapse-group, or --make-set-complement-all.\n");
+	  logerrprint("Error: --make-set-complement-group cannot be used with --complement-sets,\n--make-set-collapse-group, or --make-set-complement-all.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         set_info.modifier |= SET_COMPLEMENTS | SET_C_PREFIX | SET_MAKE_COLLAPSE_GROUP;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "erge-x", 7)) {
 	if ((chrom_info.x_code == -1) || (chrom_info.xy_code == -1)) {
-	  logprint("Error: --merge-x must be used with a chromosome set containing X and XY codes.\n");
+	  logerrprint("Error: --merge-x must be used with a chromosome set containing X and XY codes.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -9171,7 +9219,7 @@ int32_t main(int32_t argc, char** argv) {
 	if ((family_info.mendel_max_trio_error < 1.0) || (family_info.mendel_max_var_error < 1.0)) {
 	  if (covar_fname && (family_info.mendel_max_trio_error < 1.0)) {
 	    // corner case: --me screws up covariate filtered indices
-	    logprint("Error: --covar cannot be used with --me.\n");
+	    logerrprint("Error: --covar cannot be used with --me.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  // silently skip if both parameters are one, for backward
@@ -9181,7 +9229,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "e-exclude-one", 14)) {
 	if (!(family_info.mendel_modifier & MENDEL_FILTER)) {
-	  logprint("Error: --me-exclude-one must be used with a --me parameter smaller than one.\n");
+	  logerrprint("Error: --me-exclude-one must be used with a --me parameter smaller than one.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -9200,8 +9248,17 @@ int32_t main(int32_t argc, char** argv) {
 	family_info.tdt_modifier |= TDT_POOPERM_MAT;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "endel", 6)) {
+	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
+	  goto main_ret_INVALID_CMDLINE_2A;
+	}
+	if (param_ct) {
+	  if (strcmp(argv[cur_arg + 1], "summaries-only")) {
+	    sprintf(logbuf, "Error: Invalid --mendel parameter '%s'.\n", argv[cur_arg + 1]);
+	    goto main_ret_INVALID_CMDLINE;
+	  }
+	  family_info.mendel_modifier |= MENDEL_SUMMARIES_ONLY;
+	}
 	calculation_type |= CALC_MENDEL;
-        goto main_param_zero;
       } else if (!memcmp(argptr2, "endel-duos", 11)) {
 	family_info.mendel_modifier |= MENDEL_DUOS;
 	goto main_param_zero;
@@ -9226,7 +9283,7 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if ((argv[cur_arg + uii][0] == '+') && (!argv[cur_arg + uii][1])) {
 	    if (uii <= 2) {
-	      logprint("Error: --meta-analysis requires at least two PLINK report files.\n");
+	      logerrprint("Error: --meta-analysis requires at least two PLINK report files.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    break;
@@ -9252,16 +9309,16 @@ int32_t main(int32_t argc, char** argv) {
 	  } else if (!strcmp(argv[cur_arg + uii], "weighted-z")) {
 	    metaanal_flags |= METAANAL_WEIGHTED_Z;
 	  } else {
-	    sprintf(logbuf, "Error: Invalid --meta-analysis modifier '%s'.\n", argv[cur_arg + uii]);
+	    sprintf(logbuf, "Error: Invalid --meta-analysis parameter '%s'.\n", argv[cur_arg + uii]);
 	    goto main_ret_INVALID_CMDLINE_WWA;
 	  }
 	}
       } else if (!memcmp(argptr2, "eta-analysis-a1-field", 22)) {
         if (!metaanal_fnames) {
-	  logprint("Error: --meta-analysis-a1-field must be used with --meta-analysis.\n");
+	  logerrprint("Error: --meta-analysis-a1-field must be used with --meta-analysis.\n");
           goto main_ret_INVALID_CMDLINE;
 	} else if (metaanal_flags & METAANAL_NO_ALLELE) {
-	  logprint("Error: --meta-analysis-a1-field cannot be used with --meta-analysis\n'no-map'/'no-allele'.\n");
+	  logerrprint("Error: --meta-analysis-a1-field cannot be used with --meta-analysis\n'no-map'/'no-allele'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x10000000)) {
@@ -9273,10 +9330,10 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "eta-analysis-a2-field", 22)) {
         if (!metaanal_fnames) {
-	  logprint("Error: --meta-analysis-a2-field must be used with --meta-analysis.\n");
+	  logerrprint("Error: --meta-analysis-a2-field must be used with --meta-analysis.\n");
           goto main_ret_INVALID_CMDLINE;
 	} else if (metaanal_flags & METAANAL_NO_ALLELE) {
-	  logprint("Error: --meta-analysis-a2-field cannot be used with --meta-analysis\n'no-map'/'no-allele'.\n");
+	  logerrprint("Error: --meta-analysis-a2-field cannot be used with --meta-analysis\n'no-map'/'no-allele'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x10000000)) {
@@ -9288,7 +9345,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "eta-analysis-snp-field", 23)) {
         if (!metaanal_fnames) {
-	  logprint("Error: --meta-analysis-snp-field must be used with --meta-analysis.\n");
+	  logerrprint("Error: --meta-analysis-snp-field must be used with --meta-analysis.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x10000000)) {
@@ -9300,7 +9357,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "eta-analysis-p-field", 21)) {
         if ((!metaanal_fnames) || (!(metaanal_flags & METAANAL_WEIGHTED_Z))) {
-	  logprint("Error: --meta-analysis-p-field must be used with --meta-analysis + weighted-z.\n");
+	  logerrprint("Error: --meta-analysis-p-field must be used with --meta-analysis + weighted-z.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x10000000)) {
@@ -9312,7 +9369,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "eta-analysis-ess-field", 23)) {
         if ((!metaanal_fnames) || (!(metaanal_flags & METAANAL_WEIGHTED_Z))) {
-	  logprint("Error: --meta-analysis-ess-field must be used with --meta-analysis + weighted-z.\n");
+	  logerrprint("Error: --meta-analysis-ess-field must be used with --meta-analysis + weighted-z.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x10000000)) {
@@ -9337,23 +9394,23 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (scan_uint_defcap(argv[cur_arg + 1], &max_ac)) {
-	  sprintf(logbuf, "Error: Invalid --max-mac paramter '%s'.\n", argv[cur_arg + 1]);
+	  sprintf(logbuf, "Error: Invalid --max-mac parameter '%s'.\n", argv[cur_arg + 1]);
 	}
         if (max_ac < min_ac) {
-	  logprint("Error: --max-mac parameter cannot be smaller than --mac parameter.\n");
+	  logerrprint("Error: --max-mac parameter cannot be smaller than --mac parameter.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "lma", 4)) {
-        logprint("Error: --mlma is not implemented yet.\n");
+        logerrprint("Error: --mlma is not implemented yet.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "lma-loco", 9)) {
-        logprint("Error: --mlma-loco is not implemented yet.\n");
+        logerrprint("Error: --mlma-loco is not implemented yet.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "lma-no-adj-covar", 17)) {
-        logprint("Error: --mlma-no-adj-covar is not implemented yet.\n");
+        logerrprint("Error: --mlma-no-adj-covar is not implemented yet.\n");
         goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ishap-window", 13)) {
-        logprint("Error: --mishap-window is provisionally retired.  Contact the developers if you\nneed this function.\n");
+        logerrprint("Error: --mishap-window is provisionally retired.  Contact the developers if you\nneed this function.\n");
         goto main_ret_INVALID_CMDLINE;
       } else {
 	goto main_ret_INVALID_CMDLINE_UNRECOGNIZED;
@@ -9369,7 +9426,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "o-sex", 6)) {
 	if (filter_flags & (FILTER_BINARY_FEMALES | FILTER_BINARY_MALES)) {
-	  logprint("Error: --filter-males/--filter-females cannot be used with --no-sex.\n");
+	  logerrprint("Error: --filter-males/--filter-females cannot be used with --no-sex.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	fam_cols &= ~FAM_COL_5;
@@ -9394,13 +9451,13 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (neighbor_n2 < neighbor_n1) {
-	  logprint("Error: Second --neighbour parameter cannot be smaller than first parameter.\n");
+	  logerrprint("Error: Second --neighbour parameter cannot be smaller than first parameter.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         calculation_type |= CALC_NEIGHBOR;
       } else if (!memcmp(argptr2, "ot-chr", 7)) {
 	if (markername_from) {
-	  logprint("Error: --from cannot be used with --autosome{-xy} or --{not-}chr.\n");
+	  logerrprint("Error: --from cannot be used with --autosome{-xy} or --{not-}chr.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	// allowed:
@@ -9423,13 +9480,13 @@ int32_t main(int32_t argc, char** argv) {
 	  chrom_info.chrom_mask[uii] &= ~chrom_exclude[uii];
 	}
 	if (all_words_zero(chrom_info.chrom_mask, CHROM_MASK_INITIAL_WORDS) && ((!((misc_flags / MISC_ALLOW_EXTRA_CHROMS) & 1)) || (chrom_info.is_include_stack && (!chrom_info.incl_excl_name_stack)))) {
-	  logprint("Error: All chromosomes excluded.\n");
+	  logerrprint("Error: All chromosomes excluded.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	chrom_flag_present = 1;
       } else if (!memcmp(argptr2, "udge", 5)) {
         if (!(calculation_type & CALC_GENOME)) {
-	  logprint("Error: --nudge must be used with --genome.\n");
+	  logerrprint("Error: --nudge must be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         logprint("Note: --nudge flag deprecated.  Use '--genome nudge'.\n");
@@ -9445,13 +9502,13 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "o-snp", 6)) {
 	if (!(calculation_type & CALC_GLM)) {
-	  logprint("Error: --no-snp must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --no-snp must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (mtest_adjust) {
-	  logprint("Error: --no-snp cannot be used with --adjust.\n");
+	  logerrprint("Error: --no-snp cannot be used with --adjust.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (mperm_save & MPERM_DUMP_BEST) {
-	  logprint("Error: --no-snp cannot be used with --mperm-save.\n");
+	  logerrprint("Error: --no-snp cannot be used with --mperm-save.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if ((glm_modifier & (GLM_NO_SNP_EXCL - GLM_HETHOM - GLM_DOMINANT)) || ((glm_modifier & (GLM_HETHOM | GLM_DOMINANT)) && (!(glm_modifier & (GLM_CONDITION_DOMINANT | GLM_CONDITION_RECESSIVE))))) {
 	  sprintf(logbuf, "Error: --no-snp conflicts with a --%s modifier.\n", (glm_modifier & GLM_LOGISTIC)? "logistic" : "linear");
@@ -9462,7 +9519,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "o-x-sex", 8)) {
 	if (!(calculation_type & CALC_GLM)) {
-	  logprint("Error: --no-x-sex must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --no-x-sex must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (glm_modifier & (GLM_NO_SNP | GLM_SEX)) {
 	  sprintf(logbuf, "Error: --no-x-sex conflicts with a --%s modifier.\n", (glm_modifier & GLM_LOGISTIC)? "logistic" : "linear");
@@ -9473,7 +9530,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "op", 3)) {
 	if (!(epi_info.modifier & EPI_FAST)) {
-	  logprint("Error: --nop must be used with --fast-epistasis.\n");
+	  logerrprint("Error: --nop must be used with --fast-epistasis.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --nop flag deprecated.  Use '--fast-epistasis nop'.\n");
@@ -9520,7 +9577,7 @@ int32_t main(int32_t argc, char** argv) {
 	g_output_missing_geno_ptr = &(g_one_char_strs[((unsigned char)cc) * 2]);
       } else if (!memcmp(argptr2, "utput-missing-phenotype", 24)) {
 	if (load_rare == LOAD_RARE_DOSAGE) {
-	  logprint("Error: --output-missing-phenotype has no effect with --dosage.\n");
+	  logerrprint("Error: --output-missing-phenotype has no effect with --dosage.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9528,7 +9585,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	jj = strlen(argv[cur_arg + 1]);
 	if (jj > 31) {
-	  logprint("Error: --output-missing-phenotype string too long (max 31 chars).\n");
+	  logerrprint("Error: --output-missing-phenotype string too long (max 31 chars).\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	memcpy(output_missing_pheno, argv[cur_arg + 1], jj + 1);
@@ -9543,7 +9600,7 @@ int32_t main(int32_t argc, char** argv) {
 	logprint("Note: --oblig-clusters flag deprecated.  Use just --oblig-missing.\n");
       } else if (!memcmp(argptr2, "blig-missing", 13)) {
 	if ((geno_thresh == 1.0) && (mind_thresh == 1.0) && (!(calculation_type & CALC_MISSING_REPORT))) {
-	  logprint("Error: --oblig-missing must be used with --geno, --mind, or --missing.\n");
+	  logerrprint("Error: --oblig-missing must be used with --geno, --mind, or --missing.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (!oblig_missing_info.sample_fname) {
@@ -9551,7 +9608,7 @@ int32_t main(int32_t argc, char** argv) {
 	    goto main_ret_INVALID_CMDLINE_2A;
 	  }
 	} else if (param_ct != 1) {
-          logprint("Error: --oblig-missing requires exactly one parameter when --oblig-clusters is\nalso present.\n");
+          logerrprint("Error: --oblig-missing requires exactly one parameter when --oblig-clusters is\nalso present.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	retval = alloc_fname(&oblig_missing_info.marker_fname, argv[cur_arg + 1], argptr, 0);
@@ -9567,9 +9624,9 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "xford-single-chr", 17)) {
 	if (!(load_params & LOAD_PARAMS_OXGEN)) {
 	  if (load_params & LOAD_PARAMS_OXBGEN) {
-	    logprint("Error: --oxford-single-chr must be used with .gen input.  (Single-chromosome\n.bgen files do not require this, since they still contain chromosome codes.)\n");
+	    logerrprint("Error: --oxford-single-chr must be used with .gen input.  (Single-chromosome\n.bgen files do not require this, since they still contain chromosome codes.)\n");
 	  } else {
-	    logprint("Error: --oxford-single-chr must be used with .gen input.\n");
+	    logerrprint("Error: --oxford-single-chr must be used with .gen input.\n");
 	  }
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
@@ -9587,7 +9644,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "xford-pheno-name", 17)) {
 	if (!(load_params & LOAD_PARAMS_OX_ALL)) {
-	  logprint("Error: --oxford-pheno-name must be used with an Oxford-format fileset.\n");
+	  logerrprint("Error: --oxford-pheno-name must be used with an Oxford-format fileset.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9620,10 +9677,10 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --ped parameter too long.\n");
+	  logerrprint("Error: --ped parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	} else if (!memcmp(argv[cur_arg + 1], "-", 2)) {
-	  logprint("Error: '--ped -' is no longer supported.  Redirect to a temporary file and load\nit the usual way, or use PLINK 1.07.\n");
+	  logerrprint("Error: '--ped -' is no longer supported.  Redirect to a temporary file and load\nit the usual way, or use PLINK 1.07.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	strcpy(pedname, argv[cur_arg + 1]);
@@ -9632,7 +9689,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (makepheno_str) {
-	  logprint("Error: --pheno and --make-pheno flags cannot coexist.\n");
+	  logerrprint("Error: --pheno and --make-pheno flags cannot coexist.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	retval = alloc_fname(&phenoname, argv[cur_arg + 1], argptr, 0);
@@ -9642,14 +9699,14 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_FAM_REQ;
       } else if (!memcmp(argptr2, "heno-name", 10)) {
 	if (!phenoname) {
-	  logprint("Error: --pheno-name must be used with --pheno.\n");
+	  logerrprint("Error: --pheno-name must be used with --pheno.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (mpheno_col != 0) {
-	  logprint("Error: --mpheno and --pheno-name flags cannot coexist.\n");
+	  logerrprint("Error: --mpheno and --pheno-name flags cannot coexist.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (alloc_string(&phenoname_str, argv[cur_arg + 1])) {
@@ -9657,7 +9714,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "heno-merge", 11)) {
 	if (!phenoname) {
-	  logprint("Error: --pheno-merge must be used with --pheno.\n");
+	  logerrprint("Error: --pheno-merge must be used with --pheno.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	pheno_modifier |= PHENO_MERGE;
@@ -9667,28 +9724,28 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "arallel", 8)) {
 	if ((dist_calc_type & DISTANCE_SHAPEMASK) == DISTANCE_SQ) {
-	  logprint("Error: --parallel cannot be used with '--distance square'.  Use '--distance\nsquare0' or plain --distance instead.\n");
+	  logerrprint("Error: --parallel cannot be used with '--distance square'.  Use '--distance\nsquare0' or plain --distance instead.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if ((dist_calc_type & (DISTANCE_BIN | DISTANCE_BIN4)) && (!(dist_calc_type & DISTANCE_SHAPEMASK))) {
-	  logprint("Error: --parallel cannot be used with plain '--distance bin{4}'.  Use e.g.\n'--distance bin square0' or '--distance bin triangle' instead.\n");
+	  logerrprint("Error: --parallel cannot be used with plain '--distance bin{4}'.  Use e.g.\n'--distance bin square0' or '--distance bin triangle' instead.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if ((rel_info.modifier & REL_CALC_SHAPEMASK) == REL_CALC_SQ) {
-	  logprint("Error: --parallel cannot be used with '--make-rel square'.  Use '--make-rel\nsquare0' or plain '--make-rel' instead.\n");
+	  logerrprint("Error: --parallel cannot be used with '--make-rel square'.  Use '--make-rel\nsquare0' or plain '--make-rel' instead.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if ((rel_info.modifier & (REL_CALC_BIN | REL_CALC_BIN4)) && (!(rel_info.modifier & (REL_CALC_SHAPEMASK | REL_CALC_GRM_BIN)))) {
-	  logprint("Error: --parallel cannot be used with plain '--make-rel bin{4}'.  Use e.g.\n'--make-rel bin square0' or '--make-rel bin triangle' instead.\n");
+	  logerrprint("Error: --parallel cannot be used with plain '--make-rel bin{4}'.  Use e.g.\n'--make-rel bin square0' or '--make-rel bin triangle' instead.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (calculation_type & CALC_PLINK1_DISTANCE_MATRIX) {
-	  logprint("Error: --parallel and --distance-matrix cannot be used together.  Use\n--distance instead.\n");
+	  logerrprint("Error: --parallel and --distance-matrix cannot be used together.  Use\n--distance instead.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (calculation_type & CALC_GROUPDIST) {
-	  logprint("Error: --parallel and --groupdist cannot be used together.\n");
+	  logerrprint("Error: --parallel and --groupdist cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (calculation_type & CALC_CLUSTER) {
-	  logprint("Error: --parallel and --cluster cannot be used together.\n");
+	  logerrprint("Error: --parallel and --cluster cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (calculation_type & CALC_NEIGHBOR) {
-	  logprint("Error: --parallel and --neighbour cannot be used together.\n");
+	  logerrprint("Error: --parallel and --neighbour cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 2, 2)) {
@@ -9725,21 +9782,21 @@ int32_t main(int32_t argc, char** argv) {
 	    sprintf(logbuf, "Error: --perm cannot be used with --%s mperm.\n", (model_modifier & MODEL_ASSOC)? "assoc" : "model");
 	    goto main_ret_INVALID_CMDLINE_2A;
 	  } else {
-	    logprint("Error: --perm cannot be used with --mperm.\n");
+	    logerrprint("Error: --perm cannot be used with --mperm.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	} else if ((calculation_type & CALC_GLM) && (glm_modifier & (GLM_MPERM | GLM_NO_SNP))) {
 	  sprintf(logbuf, "Error: --perm cannot be used with --%s %s.\n", (glm_modifier & GLM_LOGISTIC)? "logistic" : "linear", (glm_modifier & GLM_MPERM)? "mperm" : "no-snp");
 	  goto main_ret_INVALID_CMDLINE_2A;
 	} else if (family_info.dfam_modifier & DFAM_MPERM) {
-	  logprint("Error: --perm cannot be used with --dfam mperm.\n");
+	  logerrprint("Error: --perm cannot be used with --dfam mperm.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (calculation_type & CALC_CMH) {
           if (cluster.modifier & CLUSTER_CMH_MPERM) {
 	    sprintf(logbuf, "Error: --perm cannot be used with --%s mperm.\n", (cluster.modifier & CLUSTER_CMH_BD)? "bd" : "mh");
 	    goto main_ret_INVALID_CMDLINE_2A;
 	  } else if (cluster.modifier & CLUSTER_CMH_PERM_BD) {
-	    logprint("Error: --perm cannot be used with --bd perm-bd.\n");
+	    logerrprint("Error: --perm cannot be used with --bd perm-bd.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	}
@@ -9763,11 +9820,11 @@ int32_t main(int32_t argc, char** argv) {
 	logprint("Note: --perm-count flag deprecated.  Use e.g. '--model perm-count'.\n");
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "2", 2)) {
-	logprint("Error: --p2 has been provisionally retired.  Contact the developers if you need\nthis functionality.\n");
+	logerrprint("Error: --p2 has been provisionally retired.  Contact the developers if you need\nthis functionality.\n");
         goto main_ret_INVALID_CMDLINE_A;
       } else if (!memcmp(argptr2, "filter", 7)) {
 	if (load_rare == LOAD_RARE_DOSAGE) {
-	  logprint("Error: --pfilter is currently ignored by --dosage.\n");
+	  logerrprint("Error: --pfilter is currently ignored by --dosage.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9778,7 +9835,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((dxx <= 0.0) || (dxx > 1.0)) {
-	  logprint("Error: --pfilter threshold must be in (0, 1].\n");
+	  logerrprint("Error: --pfilter threshold must be in (0, 1].\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	pfilter = dxx;
@@ -9792,7 +9849,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "pc", 3)) {
 	if (!(calculation_type & (CALC_NEIGHBOR | CALC_CLUSTER))) {
-          logprint("Error: --ppc must be used with --cluster or --neigbour.\n");
+          logerrprint("Error: --ppc must be used with --cluster or --neigbour.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9803,13 +9860,13 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((dxx <= 0.0) || (dxx >= 1.0)) {
-	  logprint("Error: --ppc threshold must be between 0 and 1 exclusive.\n");
+	  logerrprint("Error: --ppc threshold must be between 0 and 1 exclusive.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         cluster.ppc = dxx;
       } else if (!memcmp(argptr2, "ool-size", 9)) {
 	if (!(homozyg.modifier & (HOMOZYG_GROUP | HOMOZYG_GROUP_VERBOSE))) {
-          logprint("Error: --pool-size must be used with --homozyg group{-verbose}.\n");
+          logerrprint("Error: --pool-size must be used with --homozyg group{-verbose}.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9822,7 +9879,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "arameters", 10)) {
 	if (!(calculation_type & CALC_GLM)) {
 	  // drop --dosage since --covar-number has same functionality
-	  logprint("Error: --parameters must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --parameters must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	retval = parse_name_ranges(param_ct, '-', &(argv[cur_arg]), &parameters_range_list, 1);
@@ -9831,14 +9888,14 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ca", 3)) {
 #ifdef NOLAPACK
-        logprint("Error: --pca requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
+        logerrprint("Error: --pca requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
 	goto main_ret_INVALID_CMDLINE;
 #else
 	if (rel_info.modifier & REL_CALC_COV) {
-	  logprint("Error: --pca flag cannot coexist with a covariance matrix calculation.\n");
+	  logerrprint("Error: --pca flag cannot coexist with a covariance matrix calculation.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (parallel_tot > 1) {
-	  logprint("Error: --parallel and --pca cannot be used together.\n");
+	  logerrprint("Error: --parallel and --pca cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 4)) {
@@ -9854,11 +9911,11 @@ int32_t main(int32_t argc, char** argv) {
             rel_info.modifier |= REL_PCA_VAR_WTS;
 	  } else {
 	    if (ujj || scan_posint_defcap(argv[cur_arg + uii], &rel_info.pc_ct)) {
-	      logprint("Error: Invalid --pca parameter sequence.\n");
+	      logerrprint("Error: Invalid --pca parameter sequence.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (rel_info.pc_ct > 8000) {
-	      logprint("Error: --pca does not support more than 8000 PCs.\n");
+	      logerrprint("Error: --pca does not support more than 8000 PCs.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    ujj = 1;
@@ -9868,7 +9925,7 @@ int32_t main(int32_t argc, char** argv) {
 #endif
       } else if (!memcmp(argptr2, "ca-cluster-names", 17)) {
 	if (!(calculation_type & CALC_PCA)) {
-	  logprint("Error: --pca-cluster-names must be used with --pca.\n");
+	  logerrprint("Error: --pca-cluster-names must be used with --pca.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x7fffffff)) {
@@ -9880,7 +9937,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ca-clusters", 12)) {
 	if (!(calculation_type & CALC_PCA)) {
-	  logprint("Error: --pca-clusters must be used with --pca.\n");
+	  logerrprint("Error: --pca-clusters must be used with --pca.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9921,7 +9978,7 @@ int32_t main(int32_t argc, char** argv) {
 	// a PLINK 2.1 which includes a port of its imputation routine, and
 	// that can be written to handle Y/MT in a sane manner.  But that's not
 	// happening before 2016.)
-        logprint("Error: PLINK 1 proxy association and imputation commands have been retired due\nto poor accuracy.  (See Nothnagel M et al. (2009) A comprehensive evaluation of\nSNP genotype imputation.)  We suggest using another tool, such as BEAGLE 4 or\nIMPUTE2, for imputation instead, and performing association analysis on those\nresults.  ('--recode vcf' and --vcf can be used to exchange data with BEAGLE 4,\nwhile '--recode oxford' and --data let you work with IMPUTE2.)\n");
+        logerrprint("Error: PLINK 1 proxy association and imputation commands have been retired due\nto poor accuracy.  (See Nothnagel M et al. (2009) A comprehensive evaluation of\nSNP genotype imputation.)  We suggest using another tool, such as BEAGLE 4 or\nIMPUTE2, for imputation instead, and performing association analysis on those\nresults.  ('--recode vcf' and --vcf can be used to exchange data with BEAGLE 4,\nwhile '--recode oxford' and --data let you work with IMPUTE2.)\n");
         goto main_ret_INVALID_CMDLINE;
       } else {
 	goto main_ret_INVALID_CMDLINE_UNRECOGNIZED;
@@ -9931,11 +9988,11 @@ int32_t main(int32_t argc, char** argv) {
     case 'q':
       if (!memcmp(argptr2, "t-means", 8)) {
 	if ((!(calculation_type & CALC_MODEL)) || (!(model_modifier & MODEL_ASSOC))) {
-	  logprint("Error: --qt-means must be used with --assoc.\n");
+	  logerrprint("Error: --qt-means must be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (model_modifier & MODEL_DMASK) {
-	  logprint("Error: --qt-means does not make sense with a case/control-specific --assoc\nmodifier.\n");
+	  logerrprint("Error: --qt-means does not make sense with a case/control-specific --assoc\nmodifier.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --qt-means flag deprecated.  Use '--assoc qt-means ...'.\n");
@@ -9943,7 +10000,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "q-plot", 7)) {
         if (!mtest_adjust) {
-	  logprint("Error: --qq-plot must be used with --adjust.\n");
+	  logerrprint("Error: --qq-plot must be used with --adjust.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --qq-plot flag deprecated.  Use '--adjust qq-plot'.\n");
@@ -9951,7 +10008,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "match", 6)) {
         if (!(calculation_type & CALC_CLUSTER)) {
-          logprint("Error: --qmatch must be used with --cluster.\n");
+          logerrprint("Error: --qmatch must be used with --cluster.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -9968,7 +10025,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "t", 2)) {
         if (!cluster.qmatch_fname) {
-          logprint("Error: --qt must be used with --qmatch.\n");
+          logerrprint("Error: --qt must be used with --qmatch.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -9990,7 +10047,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "-score-range", 13)) {
 	if (score_info.data_fname) {
 	  if (param_ct != 1) {
-	    logprint("Error: --q-score-range must be given exactly one parameter when --q-score-file\nis present.\n");
+	    logerrprint("Error: --q-score-range must be given exactly one parameter when --q-score-file\nis present.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  retval = alloc_fname(&score_info.range_fname, argv[cur_arg + 1], argptr, 0);
@@ -10012,7 +10069,7 @@ int32_t main(int32_t argc, char** argv) {
 	    if (!strcmp(argv[cur_arg + uii], "header")) {
 	      score_info.modifier |= SCORE_DATA_HEADER;
 	    } else if (ujj == 2) {
-              logprint("Error: --q-score-range takes at most two numeric parameters.\n");
+              logerrprint("Error: --q-score-range takes at most two numeric parameters.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    } else {
 	      if (scan_posint_capped(argv[cur_arg + uii], (uint32_t*)&ii, (MAXLINEBUFLEN / 2) / 10, (MAXLINEBUFLEN / 2) % 10)) {
@@ -10024,7 +10081,7 @@ int32_t main(int32_t argc, char** argv) {
                 score_info.data_col = ii + 1;
 	      } else {
 		if ((uint32_t)ii == score_info.data_varid_col) {
-	          logprint("Error: --q-score-range variant ID and data column numbers cannot match.\n");
+	          logerrprint("Error: --q-score-range variant ID and data column numbers cannot match.\n");
 	          goto main_ret_INVALID_CMDLINE_A;
 		}
 		score_info.data_col = ii;
@@ -10044,7 +10101,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "ual-threshold", 14)) {
 	if (!qual_filter) {
-	  logprint("Error: --qual-threshold must be used with --qual-scores.\n");
+	  logerrprint("Error: --qual-threshold must be used with --qual-scores.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -10055,7 +10112,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if (qual_min_thresh > qual_max_thresh) {
-	  logprint("Error: --qual-threshold value cannot be larger than --qual-max-threshold value.\n");
+	  logerrprint("Error: --qual-threshold value cannot be larger than --qual-max-threshold value.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "ual-max-threshold", 18)) {
@@ -10068,7 +10125,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if ((!memcmp(argptr2, "fam", 4)) || (!memcmp(argptr2, "fam-parents", 12)) || (!memcmp(argptr2, "fam-between", 12)) || (!memcmp(argptr2, "fam-total", 10))) {
 	if (calculation_type & CALC_QFAM) {
-	  logprint("Error: Only one QFAM test can be run at a time.\n");
+	  logerrprint("Error: Only one QFAM test can be run at a time.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 3)) {
@@ -10123,7 +10180,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if ((!memcmp(argptr2, "ual-geno-scores", 16)) ||
                  (!memcmp(argptr2, "ual-geno-threshold", 19)) ||
                  (!memcmp(argptr2, "ual-geno-max-threshold", 23))) {
-	logprint("Error: --qual-geno-scores has been provisionally retired, since it cannot be\nextended in a VCF-friendly manner.  Contact the developers if you prefer to\ncontinue using its interface.\n");
+	logerrprint("Error: --qual-geno-scores has been provisionally retired, since it cannot be\nextended in a VCF-friendly manner.  Contact the developers if you prefer to\ncontinue using its interface.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else {
 	goto main_ret_INVALID_CMDLINE_UNRECOGNIZED;
@@ -10169,15 +10226,15 @@ int32_t main(int32_t argc, char** argv) {
         filter_flags |= FILTER_FAM_REQ;
       } else if (!memcmp(argptr2, "el-cutoff", 10)) {
 	if (parallel_tot > 1) {
-	  logprint("Error: --parallel cannot be used with --rel-cutoff.  (Use a combination of\n--make-rel, --keep/--remove, and a filtering script.)\n");
+	  logerrprint("Error: --parallel cannot be used with --rel-cutoff.  (Use a combination of\n--make-rel, --keep/--remove, and a filtering script.)\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (rel_info.pca_cluster_names_flattened || rel_info.pca_clusters_fname) {
-	  logprint("Error: --pca-cluster-names/--pca-clusters cannot be used with --rel-cutoff.\n");
+	  logerrprint("Error: --pca-cluster-names/--pca-clusters cannot be used with --rel-cutoff.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (covar_fname) {
 	  // corner case: --rel-cutoff screws up covariate filtered indices
-          logprint("Error: --covar cannot be used with --rel-cutoff.\n");
+          logerrprint("Error: --covar cannot be used with --rel-cutoff.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 
@@ -10193,7 +10250,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_REL_CUTOFF;
       } else if (!memcmp(argptr2, "egress-distance", 16)) {
 	if (parallel_tot > 1) {
-	  logprint("Error: --parallel and --regress-distance cannot be used together.\n");
+	  logerrprint("Error: --parallel and --regress-distance cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 2)) {
@@ -10214,10 +10271,10 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_REGRESS_DISTANCE;
       } else if (!memcmp(argptr2, "egress-rel", 11)) {
 	if (parallel_tot > 1) {
-	  logprint("Error: --parallel and --regress-rel flags cannot be used together.\n");
+	  logerrprint("Error: --parallel and --regress-rel flags cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (rel_info.pca_cluster_names_flattened || rel_info.pca_clusters_fname) {
-	  logprint("Error: --pca-cluster-names/--pca-clusters cannot be used with --regress-rel.\n");
+	  logerrprint("Error: --pca-cluster-names/--pca-clusters cannot be used with --regress-rel.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 2)) {
@@ -10237,7 +10294,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	calculation_type |= CALC_REGRESS_REL;
       } else if ((!memcmp(argptr2, "egress-pcs", 11)) || (!memcmp(argptr2, "egress-pcs-distance", 20))) {
-	logprint("Error: --regress-pcs has been temporarily disabled.  Contact the developers if\nyou need a build with the old implementation unlocked.\n");
+	logerrprint("Error: --regress-pcs has been temporarily disabled.  Contact the developers if\nyou need a build with the old implementation unlocked.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto main_ret_1;
 	/*
@@ -10330,19 +10387,19 @@ int32_t main(int32_t argc, char** argv) {
 	    dist_calc_type |= DISTANCE_BIN;
 	  } else if (!strcmp(argv[cur_arg + uii], "ibs")) {
 	    if (dist_calc_type & DISTANCE_IBS) {
-	      logprint("Error: Duplicate --regress-pcs-distance 'ibs' modifier.\n");
+	      logerrprint("Error: Duplicate --regress-pcs-distance 'ibs' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dist_calc_type |= DISTANCE_IBS;
 	  } else if (!strcmp(argv[cur_arg + uii], "1-ibs")) {
 	    if (dist_calc_type & DISTANCE_1_MINUS_IBS) {
-	      logprint("Error: Duplicate --regress-pcs-distance '1-ibs' modifier.\n");
+	      logerrprint("Error: Duplicate --regress-pcs-distance '1-ibs' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dist_calc_type |= DISTANCE_1_MINUS_IBS;
 	  } else if (!strcmp(argv[cur_arg + uii], "allele-ct")) {
 	    if (dist_calc_type & DISTANCE_ALCT) {
-	      logprint("Error: Duplicate --regress-pcs-distance 'allele-ct' modifier.\n");
+	      logerrprint("Error: Duplicate --regress-pcs-distance 'allele-ct' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dist_calc_type |= DISTANCE_ALCT;
@@ -10365,10 +10422,10 @@ int32_t main(int32_t argc, char** argv) {
 	*/
       } else if (!memcmp(argptr2, "ead-freq", 9)) {
 	if (calculation_type & CALC_FREQ) {
-	  logprint("Error: --freq and --read-freq flags cannot coexist.\n");
+	  logerrprint("Error: --freq and --read-freq flags cannot coexist.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (misc_flags & MISC_HET_SMALL_SAMPLE) {
-	  logprint("Error: '--het small-sample' cannot currently be used with --read-freq.  Contact\nthe developers if you need to combine them.\n");
+	  logerrprint("Error: '--het small-sample' cannot currently be used with --read-freq.  Contact\nthe developers if you need to combine them.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -10391,22 +10448,22 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if ((!strcmp(argv[cur_arg + uii], "01")) || (!strcmp(argv[cur_arg + uii], "12"))) {
 	    if (recode_modifier & (RECODE_A | RECODE_AD)) {
-	      logprint("Error: --recode '01' and '12' modifiers cannot be used with 'A' or 'AD'.\n");
+	      logerrprint("Error: --recode '01' and '12' modifiers cannot be used with 'A' or 'AD'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (recode_modifier & RECODE_VCF) {
 	    main_recode_012_vcf_conflict:
-	      logprint("Error: --recode '01' and '12' modifiers cannot be used with VCF output.\n");
+	      logerrprint("Error: --recode '01' and '12' modifiers cannot be used with VCF output.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    if (argv[cur_arg + uii][0] == '0') {
 	      if (recode_modifier & RECODE_12) {
-		logprint("Error: --recode '01' and '12' modifiers cannot be used together.\n");
+		logerrprint("Error: --recode '01' and '12' modifiers cannot be used together.\n");
 		goto main_ret_INVALID_CMDLINE;
 	      }
 	      recode_modifier |= RECODE_01;
 	    } else {
 	      if (recode_modifier & RECODE_01) {
-		logprint("Error: --recode '01' and '12' modifiers cannot be used together.\n");
+		logerrprint("Error: --recode '01' and '12' modifiers cannot be used together.\n");
 		goto main_ret_INVALID_CMDLINE;
 	      }
 	      recode_modifier |= RECODE_12;
@@ -10443,19 +10500,19 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	  } else if (!strcmp(argv[cur_arg + uii], "tab")) {
 	    if (recode_modifier & (RECODE_TAB | RECODE_DELIMX)) {
-	      logprint("Error: Multiple --recode delimiter modifiers.\n");
+	      logerrprint("Error: Multiple --recode delimiter modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    recode_modifier |= RECODE_TAB;
 	  } else if (!strcmp(argv[cur_arg + uii], "tabx")) {
 	    if (recode_modifier & (RECODE_TAB | RECODE_DELIMX)) {
-	      logprint("Error: Multiple --recode delimiter modifiers.\n");
+	      logerrprint("Error: Multiple --recode delimiter modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    recode_modifier |= RECODE_TAB | RECODE_DELIMX;
 	  } else if (!strcmp(argv[cur_arg + uii], "spacex")) {
 	    if (recode_modifier & (RECODE_TAB | RECODE_DELIMX)) {
-	      logprint("Error: Multiple --recode delimiter modifiers.\n");
+	      logerrprint("Error: Multiple --recode delimiter modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    recode_modifier |= RECODE_DELIMX;
@@ -10518,7 +10575,7 @@ int32_t main(int32_t argc, char** argv) {
 	  } else if (!strcmp(argv[cur_arg + uii], "vcf")) {
 	    if (recode_modifier & RECODE_VCF) {
 	    main_recode_vcf_conflict:
-	      logprint("Error: Conflicting or redundant --recode modifiers.\n");
+	      logerrprint("Error: Conflicting or redundant --recode modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (recode_modifier & (RECODE_01 | RECODE_12)) {
 	      goto main_recode_012_vcf_conflict;
@@ -10548,34 +10605,40 @@ int32_t main(int32_t argc, char** argv) {
 	    recode_modifier |= RECODE_IID;
 	  } else if (!strcmp(argv[cur_arg + uii], "include-alt")) {
 	    recode_modifier |= RECODE_INCLUDE_ALT;
+	  } else if (!strcmp(argv[cur_arg + uii], "omit-nonmale-y")) {
+	    recode_modifier |= RECODE_OMIT_NONMALE_Y;
 	  } else {
 	    sprintf(logbuf, "Error: Invalid --recode parameter '%s'.%s\n", argv[cur_arg + uii], ((uii == param_ct) && (!outname_end))? "  (Did you forget '--out'?)" : "");
 	    goto main_ret_INVALID_CMDLINE_WWA;
 	  }
 	}
 	if ((recode_modifier & RECODE_INCLUDE_ALT) && (!(recode_modifier & (RECODE_A | RECODE_AD)))) {
-	  logprint("Error: --recode 'include-alt' modifier must be used with 'A' or 'AD'.\n");
+	  logerrprint("Error: --recode 'include-alt' modifier must be used with 'A' or 'AD'.\n");
+	  goto main_ret_INVALID_CMDLINE_A;
+	}
+	if ((recode_modifier & RECODE_OMIT_NONMALE_Y) && (!(recode_modifier & (RECODE_LIST | RECODE_RLIST)))) {
+	  logerrprint("Error: --recode 'omit-nonmale-y' modifier must be used with 'list' or 'rlist'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((recode_modifier & RECODE_BGZ) && (!(recode_modifier & RECODE_VCF))) {
-	  logprint("Error: --recode 'bgz' modifier must be used with VCF output.\n");
+	  logerrprint("Error: --recode 'bgz' modifier must be used with VCF output.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((recode_modifier & RECODE_GEN_GZ) && (!(recode_modifier & RECODE_OXFORD))) {
-	  logprint("Error: --recode 'gen-gz' modifier must be used with Oxford-format output.\n");
+	  logerrprint("Error: --recode 'gen-gz' modifier must be used with Oxford-format output.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	calculation_type |= CALC_RECODE;
       } else if (!memcmp(argptr2, "ecode-whap", 11)) {
-        logprint("Error: --recode-whap flag retired since WHAP is no longer supported.\n");
+        logerrprint("Error: --recode-whap flag retired since WHAP is no longer supported.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ecode-allele", 13)) {
 	if (!(recode_modifier & (RECODE_A | RECODE_A_TRANSPOSE | RECODE_AD))) {
-	  logprint("Error: --recode-allele must be used with --recode A/A-transpose/AD.\n");
+	  logerrprint("Error: --recode-allele must be used with --recode A/A-transpose/AD.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (recode_modifier & (RECODE_01 | RECODE_12)) {
-	  logprint("Error: --recode-allele cannot be used with --recode 01/12.\n");
+	  logerrprint("Error: --recode-allele cannot be used with --recode 01/12.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -10596,13 +10659,13 @@ int32_t main(int32_t argc, char** argv) {
 	lgen_modifier |= LGEN_REFERENCE;
       } else if (!memcmp(argptr2, "ead-genome", 11)) {
 	if (calculation_type & CALC_GENOME) {
-          logprint("Error: --read-genome cannot be used with --genome.\n");
+          logerrprint("Error: --read-genome cannot be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (!(calculation_type & (CALC_CLUSTER | CALC_NEIGHBOR))) {
-          logprint("Error: --read-genome cannot be used without --cluster or --neighbour.\n");
+          logerrprint("Error: --read-genome cannot be used without --cluster or --neighbour.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	} else if ((cluster.ppc == 0.0) && ((calculation_type & (CALC_DISTANCE | CALC_PLINK1_DISTANCE_MATRIX)) || read_dists_fname)) {
-          logprint("Error: --read-genome is pointless with --distance, --distance-matrix, and\n--read-dists unless --ppc is also present.\n");
+          logerrprint("Error: --read-genome is pointless with --distance, --distance-matrix, and\n--read-dists unless --ppc is also present.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -10613,17 +10676,17 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_1;
 	}
       } else if (!memcmp(argptr2, "ead-genome-list", 19)) {
-	logprint("Error: --read-genome-list flag retired.  Use --parallel + Unix cat instead.\n");
+	logerrprint("Error: --read-genome-list flag retired.  Use --parallel + Unix cat instead.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ead-genome-minimal", 19)) {
-	logprint("Error: --read-genome-minimal flag retired.  Use --genome gz + --read-genome\ninstead.");
+	logerrprint("Error: --read-genome-minimal flag retired.  Use --genome gz + --read-genome\ninstead.");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "ead-dists", 10)) {
 	if (calculation_type & (CALC_DISTANCE | CALC_PLINK1_DISTANCE_MATRIX)) {
-	  logprint("Error: --read-dists cannot be used with a distance matrix calculation.\n");
+	  logerrprint("Error: --read-dists cannot be used with a distance matrix calculation.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (cluster.modifier & CLUSTER_MISSING) {
-          logprint("Error: --read-dists cannot be used with '--cluster missing'.\n");
+          logerrprint("Error: --read-dists cannot be used with '--cluster missing'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -10641,7 +10704,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "el-check", 9)) {
         if (!(calculation_type & CALC_GENOME)) {
-          logprint("Error: --rel-check must be used with --genome.\n");
+          logerrprint("Error: --rel-check must be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         logprint("Note: --rel-check flag deprecated.  Use '--genome rel-check'.\n");
@@ -10649,7 +10712,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "ecessive", 9)) {
 	if (!(calculation_type & CALC_GLM)) {
-	  logprint("Error: --recessive must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --recessive must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (glm_modifier & (GLM_GENOTYPIC | GLM_HETHOM | GLM_DOMINANT)) {
 	  sprintf(logbuf, "Error: --recessive conflicts with a --%s modifier.\n", (glm_modifier & GLM_LOGISTIC)? "logistic" : "linear");
@@ -10661,12 +10724,12 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if ((*argptr2 == '\0') || (!memcmp(argptr2, "2", 2))) {
 	if (calculation_type & CALC_LD) {
-          logprint("Error: --r and --r2 cannot be used together.\n");
+          logerrprint("Error: --r and --r2 cannot be used together.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (calculation_type & CALC_BLOCKS) {
 	  // prevent --ld-window-r2 conflict
-	  logprint("Error: --r/--r2 cannot be used with --blocks.\n");
+	  logerrprint("Error: --r/--r2 cannot be used with --blocks.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 6)) {
@@ -10675,7 +10738,7 @@ int32_t main(int32_t argc, char** argv) {
 	if (*argptr2 == '2') {
           ld_info.modifier |= LD_R2;
 	} else if (ld_info.window_r2 != 0.2) {
-	  logprint("Error: --ld-window-r2 flag cannot be used with --r.\n");
+	  logerrprint("Error: --ld-window-r2 flag cannot be used with --r.\n");
 	}
 	if (matrix_flag_state) {
 	  matrix_flag_state = 2;
@@ -10684,7 +10747,7 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
           if (!strcmp(argv[cur_arg + uii], "square")) {
 	    if (ld_info.modifier & LD_MATRIX_SHAPEMASK) {
-	      logprint("Error: Multiple --r/--r2 shape modifiers.\n");
+	      logerrprint("Error: Multiple --r/--r2 shape modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (ld_info.modifier & (LD_INTER_CHR | LD_INPHASE | LD_DPRIME | LD_WITH_FREQS)) {
 	    main_r2_matrix_conflict:
@@ -10694,7 +10757,7 @@ int32_t main(int32_t argc, char** argv) {
 	    ld_info.modifier |= LD_MATRIX_SQ;
 	  } else if (!strcmp(argv[cur_arg + uii], "square0")) {
 	    if (ld_info.modifier & LD_MATRIX_SHAPEMASK) {
-	      logprint("Error: Multiple --r/--r2 shape modifiers.\n");
+	      logerrprint("Error: Multiple --r/--r2 shape modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (ld_info.modifier & (LD_INTER_CHR | LD_INPHASE | LD_DPRIME | LD_WITH_FREQS)) {
 	      goto main_r2_matrix_conflict;
@@ -10702,7 +10765,7 @@ int32_t main(int32_t argc, char** argv) {
 	    ld_info.modifier |= LD_MATRIX_SQ0;
 	  } else if (!strcmp(argv[cur_arg + uii], "triangle")) {
 	    if (ld_info.modifier & LD_MATRIX_SHAPEMASK) {
-	      logprint("Error: Multiple --r/--r2 shape modifiers.\n");
+	      logerrprint("Error: Multiple --r/--r2 shape modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (ld_info.modifier & (LD_INTER_CHR | LD_INPHASE | LD_DPRIME | LD_WITH_FREQS)) {
 	      goto main_r2_matrix_conflict;
@@ -10715,7 +10778,7 @@ int32_t main(int32_t argc, char** argv) {
 	    }
 	  } else if (!strcmp(argv[cur_arg + uii], "gz")) {
 	    if (ld_info.modifier & (LD_MATRIX_BIN | LD_MATRIX_BIN4)) {
-	      logprint("Error: Conflicting --r/--r2 modifiers.\n");
+	      logerrprint("Error: Conflicting --r/--r2 modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    ld_info.modifier |= LD_REPORT_GZ;
@@ -10723,10 +10786,10 @@ int32_t main(int32_t argc, char** argv) {
 	    if (ld_info.modifier & (LD_INTER_CHR | LD_INPHASE | LD_DPRIME | LD_WITH_FREQS)) {
 	      goto main_r2_matrix_conflict;
 	    } else if (ld_info.modifier & (LD_REPORT_GZ | LD_MATRIX_BIN4)) {
-	      logprint("Error: Conflicting --r/--r2 modifiers.\n");
+	      logerrprint("Error: Conflicting --r/--r2 modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (ld_info.modifier & LD_MATRIX_SPACES) {
-	      logprint("Error: --r/--r2 'bin{4}' and 'spaces' modifiers cannot be used together.\n");
+	      logerrprint("Error: --r/--r2 'bin{4}' and 'spaces' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    ld_info.modifier |= LD_MATRIX_BIN;
@@ -10734,21 +10797,21 @@ int32_t main(int32_t argc, char** argv) {
 	    if (ld_info.modifier & (LD_INTER_CHR | LD_INPHASE | LD_DPRIME | LD_WITH_FREQS)) {
 	      goto main_r2_matrix_conflict;
 	    } else if (ld_info.modifier & (LD_REPORT_GZ | LD_MATRIX_BIN)) {
-	      logprint("Error: Conflicting --r/--r2 modifiers.\n");
+	      logerrprint("Error: Conflicting --r/--r2 modifiers.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (ld_info.modifier & LD_MATRIX_SPACES) {
-	      logprint("Error: --r/--r2 'bin{4}' and 'spaces' modifiers cannot be used together.\n");
+	      logerrprint("Error: --r/--r2 'bin{4}' and 'spaces' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    ld_info.modifier |= LD_MATRIX_BIN4;
 	  } else if (!strcmp(argv[cur_arg + uii], "single-prec")) {
-	    logprint("Error: --r/--r2 'single-prec' modifier has been retired.  Use 'bin4'.\n");
+	    logerrprint("Error: --r/--r2 'single-prec' modifier has been retired.  Use 'bin4'.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else if (!strcmp(argv[cur_arg + uii], "spaces")) {
 	    if (ld_info.modifier & (LD_INTER_CHR | LD_INPHASE | LD_DPRIME | LD_WITH_FREQS)) {
 	      goto main_r2_matrix_conflict;
 	    } else if (ld_info.modifier & (LD_MATRIX_BIN | LD_MATRIX_BIN4)) {
-	      logprint("Error: --r/--r2 'bin{4}' and 'spaces' modifiers cannot be used together.\n");
+	      logerrprint("Error: --r/--r2 'bin{4}' and 'spaces' modifiers cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    ld_info.modifier |= LD_MATRIX_SPACES;
@@ -10778,14 +10841,14 @@ int32_t main(int32_t argc, char** argv) {
           ld_info.modifier |= LD_MATRIX_SQ;
 	}
 	if ((ld_info.modifier & LD_MATRIX_SPACES) && (!(ld_info.modifier & LD_MATRIX_SHAPEMASK))) {
-	  logprint("Error: --r/--r2 'spaces' modifier must be used with a shape modifier.\n");
+	  logerrprint("Error: --r/--r2 'spaces' modifier must be used with a shape modifier.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((ld_info.modifier & LD_WEIGHTED_X) && (ld_info.modifier & (LD_MATRIX_SHAPEMASK | LD_INTER_CHR))) {
-	  logprint("Error: --ld-xchr 3 cannot be used with --r/--r2 non-windowed reports.\n");
+	  logerrprint("Error: --ld-xchr 3 cannot be used with --r/--r2 non-windowed reports.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	} else if (ld_info.modifier & LD_WEIGHTED_X) {
-	  logprint("Error: --r/--r2 + --ld-xchr 3 is not currently supported.\n");
+	  logerrprint("Error: --r/--r2 + --ld-xchr 3 is not currently supported.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	calculation_type |= CALC_LD;
@@ -10800,7 +10863,7 @@ int32_t main(int32_t argc, char** argv) {
         if (extractname) {
 	  misc_flags |= MISC_EXTRACT_RANGE;
 	} else if (!excludename) {
-	  logprint("Error: --range must be used with --extract and/or --exclude.\n");
+	  logerrprint("Error: --range must be used with --extract and/or --exclude.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (excludename) {
@@ -10830,7 +10893,7 @@ int32_t main(int32_t argc, char** argv) {
 	if ((load_params & (LOAD_PARAMS_TEXT_ALL | LOAD_PARAMS_BFILE_ALL)) || load_rare) {
 	  goto main_ret_INVALID_CMDLINE_INPUT_CONFLICT;
 	} else if (!(load_params & LOAD_PARAMS_OX_ALL)) {
-	  logprint("Error: --sample cannot be used without --data, --gen, or --bgen.\n");
+	  logerrprint("Error: --sample cannot be used without --data, --gen, or --bgen.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	load_params |= LOAD_PARAMS_OXSAMPLE;
@@ -10838,25 +10901,25 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (strlen(argv[cur_arg + 1]) > (FNAMESIZE - 1)) {
-	  logprint("Error: --sample parameter too long.\n");
+	  logerrprint("Error: --sample parameter too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	strcpy(mapname, argv[cur_arg + 1]);
       } else if (!memcmp(argptr2, "np", 3)) {
         if (markername_from) {
-	  logprint("Error: --snp cannot be used with --from.\n");
+	  logerrprint("Error: --snp cannot be used with --from.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (marker_pos_start != -1) {
-	  logprint("Error: --snp cannot be used with --from-bp/-kb/-mb.\n");
+	  logerrprint("Error: --snp cannot be used with --from-bp/-kb/-mb.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if ((!all_words_zero(chrom_info.chrom_mask, CHROM_MASK_INITIAL_WORDS)) || chrom_info.incl_excl_name_stack) {
-	  logprint("Error: --snp cannot be used with --autosome{-xy} or --{not-}chr.\n");
+	  logerrprint("Error: --snp cannot be used with --autosome{-xy} or --{not-}chr.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (markername_snp) {
-          logprint("Error: --snp cannot be used with --exclude-snp.\n");
+          logerrprint("Error: --snp cannot be used with --exclude-snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (snps_range_list.names) {
-          logprint("Error: --snp cannot be used with --exclude-snps.\n");
+          logerrprint("Error: --snp cannot be used with --exclude-snps.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -10868,16 +10931,16 @@ int32_t main(int32_t argc, char** argv) {
         filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "nps", 4)) {
 	if (markername_from) {
-	  logprint("Error: --snps cannot be used with --from.\n");
+	  logerrprint("Error: --snps cannot be used with --from.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (marker_pos_start != -1) {
-	  logprint("Error: --snps cannot be used with --from-bp/-kb/-mb.\n");
+	  logerrprint("Error: --snps cannot be used with --from-bp/-kb/-mb.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (markername_snp) {
-	  logprint("Error: --snps cannot be used with --snp or --exclude-snp.\n");
+	  logerrprint("Error: --snps cannot be used with --snp or --exclude-snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (snps_range_list.names) {
-	  logprint("Error: --snps cannot be used with --exclude-snps.\n");
+	  logerrprint("Error: --snps cannot be used with --exclude-snps.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	// mise well allow --snps + --autosome/--autosome-xy/--chr/--not-chr
@@ -10891,7 +10954,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "et", 3)) {
 	if (set_info.fname) {
-	  logprint("Error: --set cannot be used with --make-set.\n");
+	  logerrprint("Error: --set cannot be used with --make-set.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
@@ -10908,13 +10971,13 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ;
       } else if (!memcmp(argptr2, "et-collapse-all", 16)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-collapse-all must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-collapse-all must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (set_info.modifier & SET_MAKE_COLLAPSE_GROUP) {
-	  logprint("Error: --set-collapse-all cannot be used with --make-set-collapse-group or\n--make-set-complement-group.\n");
+	  logerrprint("Error: --set-collapse-all cannot be used with --make-set-collapse-group or\n--make-set-complement-group.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (set_info.merged_set_name) {
-	  logprint("Error: --set-collapse-all cannot be used with --make-set-complement-all.\n");
+	  logerrprint("Error: --set-collapse-all cannot be used with --make-set-complement-all.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -10925,7 +10988,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "et-names", 9)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-names must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-names must be used with --set/--make-set.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 0x7fffffff)) {
@@ -10940,7 +11003,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (!set_info.fname) {
-	  logprint("Error: --subset must be used with --set/--make-set.\n");
+	  logerrprint("Error: --subset must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	retval = alloc_fname(&set_info.subset_fname, argv[cur_arg + 1], argptr, 0);
@@ -11003,7 +11066,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (load_rare != LOAD_RARE_SIMULATE) {
-	  logprint("Error: --simulate-ncases must be used with --simulate.\n");
+	  logerrprint("Error: --simulate-ncases must be used with --simulate.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (scan_uint_defcap(argv[cur_arg + 1], &simulate_cases)) {
@@ -11023,7 +11086,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
 	if ((!simulate_controls) && (!simulate_cases)) {
-	  logprint("Error: '--simulate-ncases 0' cannot be used with '--simulate-ncontrols 0'.\n");
+	  logerrprint("Error: '--simulate-ncases 0' cannot be used with '--simulate-ncontrols 0'.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "imulate-prevalence", 19)) {
@@ -11054,7 +11117,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (load_rare == LOAD_RARE_SIMULATE) {
-	  logprint("Error: --simulate-n must be used with --simulate-qt, not --simulate.\n");
+	  logerrprint("Error: --simulate-n must be used with --simulate-qt, not --simulate.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (scan_posint_defcap(argv[cur_arg + 1], &simulate_qt_samples)) {
@@ -11063,7 +11126,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "imulate-haps", 13)) {
 	if (simulate_flags & SIMULATE_TAGS) {
-	  logprint("Error: --simulate-tags cannot be used with --simulate-haps.\n");
+	  logerrprint("Error: --simulate-tags cannot be used with --simulate-haps.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --simulate-haps flag deprecated.  Use e.g. '--simulate haps'.\n");
@@ -11071,7 +11134,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "imulate-tags", 13)) {
 	if (simulate_flags & SIMULATE_HAPS) {
-	  logprint("Error: --simulate-tags cannot be used with --simulate-haps.\n");
+	  logerrprint("Error: --simulate-tags cannot be used with --simulate-haps.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --simulate-tags flag deprecated.  Use e.g. '--simulate tags'.\n");
@@ -11082,7 +11145,7 @@ int32_t main(int32_t argc, char** argv) {
 	  dosage_info.modifier |= DOSAGE_SEX;
 	} else {
 	  if (!(calculation_type & CALC_GLM)) {
-	    logprint("Error: --sex must be used with --linear/--logistic/--dosage.\n");
+	    logerrprint("Error: --sex must be used with --linear/--logistic/--dosage.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if (glm_modifier & GLM_NO_X_SEX) {
 	    sprintf(logbuf, "Error: --sex conflicts with a --%s modifier.\n", (glm_modifier & GLM_LOGISTIC)? "logistic" : "linear");
@@ -11094,7 +11157,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "tandard-beta", 13)) {
 	if (((!(calculation_type & CALC_GLM)) || (glm_modifier & GLM_LOGISTIC)) && (!(dosage_info.modifier & DOSAGE_GLM))) {
-	  logprint("Error: --standard-beta must be used wtih --linear or --dosage.\n");
+	  logerrprint("Error: --standard-beta must be used wtih --linear or --dosage.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --standard-beta flag deprecated.  Use e.g. '--linear standard-beta'.\n");
@@ -11102,7 +11165,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "et-table", 9)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-table must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-table must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         calculation_type |= CALC_WRITE_SET;
@@ -11110,7 +11173,7 @@ int32_t main(int32_t argc, char** argv) {
         goto main_param_zero;
       } else if (!memcmp(argptr2, "et-test", 8)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-test must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-test must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --set-test flag deprecated.  Use e.g. '--assoc perm set-test'.\n");
@@ -11119,7 +11182,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	if (calculation_type & CALC_GLM) {
 	  if (glm_modifier & GLM_NO_SNP) {
-	    logprint("Error: --set-test cannot be used with --linear/--logistic no-snp.\n");
+	    logerrprint("Error: --set-test cannot be used with --linear/--logistic no-snp.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  glm_modifier |= GLM_SET_TEST;
@@ -11139,7 +11202,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "et-p", 5)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-p must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-p must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11152,7 +11215,7 @@ int32_t main(int32_t argc, char** argv) {
 	set_info.set_p = dxx;
       } else if (!memcmp(argptr2, "et-r2", 5)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-r2 must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-r2 must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -11184,11 +11247,11 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	}
       } else if (!memcmp(argptr2, "et-r2-phase", 11)) {
-        logprint("Error: --set-r2-phase is provisionally retired.  Contact the developers if you\nneed this function.\n");
+        logerrprint("Error: --set-r2-phase is provisionally retired.  Contact the developers if you\nneed this function.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "et-max", 5)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-max must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-max must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11200,7 +11263,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "et-test-lambda", 15)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-test-lambda must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-test-lambda must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11216,11 +11279,11 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "et-by-all", 10)) {
 	if (!set_info.fname) {
-	  logprint("Error: --set-by-all must be used with --set/--make-set.\n");
+	  logerrprint("Error: --set-by-all must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (!(epi_info.modifier & (EPI_FAST | EPI_REG))) {
-	  logprint("Error: --set-by-all must be used with --{fast-}epistasis.\n");
+	  logerrprint("Error: --set-by-all must be used with --{fast-}epistasis.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --set-by-all flag deprecated.  Use e.g. '--fast-epistasis set-by-all'.\n");
@@ -11240,10 +11303,10 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_SNPS_ONLY | FILTER_NODOSAGE | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "plit-x", 7)) {
 	if (misc_flags & MISC_MERGEX) {
-          logprint("Error: --split-x cannot be used with --merge-x.\n");
+          logerrprint("Error: --split-x cannot be used with --merge-x.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	} else if ((chrom_info.x_code == -1) || (chrom_info.xy_code == -1)) {
-	  logprint("Error: --split-x must be used with a chromosome set containing X and XY codes.\n");
+	  logerrprint("Error: --split-x must be used with a chromosome set containing X and XY codes.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 3)) {
@@ -11260,7 +11323,7 @@ int32_t main(int32_t argc, char** argv) {
 	  ujj--;
 	}
 	if ((ujj < uii) || (ujj == uii + 2)) {
-	  logprint("Error: Invalid --split-x parameter sequence.\n");
+	  logerrprint("Error: Invalid --split-x parameter sequence.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (ujj == uii) {
@@ -11278,7 +11341,7 @@ int32_t main(int32_t argc, char** argv) {
 	    goto main_ret_INVALID_CMDLINE_WWA;
 	  }
 	  if (chrom_info.species != SPECIES_HUMAN) {
-	    logprint("Error: --split-x build codes cannot be used with nonhuman chromosome sets.\n");
+	    logerrprint("Error: --split-x build codes cannot be used with nonhuman chromosome sets.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	} else {
@@ -11292,10 +11355,10 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	}
       } else if (!memcmp(argptr2, "et-missing-snp-ids", 19)) {
-	logprint("Error: --set-missing-snp-ids has been retired.  Use --set-missing-var-ids with\n'$1' and '$2' instead.\n");
+	logerrprint("Error: --set-missing-snp-ids has been retired.  Use --set-missing-var-ids with\n'$1' and '$2' instead.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       } else if (!memcmp(argptr2, "et-missing-nonsnp-ids", 22)) {
-	logprint("Error: --set-missing-nonsnp-ids has been retired.  Use --set-missing-var-ids\nwith '$1' and '$2' instead.\n");
+	logerrprint("Error: --set-missing-nonsnp-ids has been retired.  Use --set-missing-var-ids\nwith '$1' and '$2' instead.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       } else if (!memcmp(argptr2, "et-missing-var-ids", 19)) {
 	if (load_rare & (LOAD_RARE_CNV | LOAD_RARE_DOSAGE)) {
@@ -11315,15 +11378,15 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "core", 5)) {
 	if (load_rare & LOAD_RARE_DOSAGE) {
 	  if (dosage_info.modifier & DOSAGE_OCCUR) {
-	    logprint("Error: --dosage 'occur' mode cannot be used with --score.\n");
+	    logerrprint("Error: --dosage 'occur' mode cannot be used with --score.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  if (dosage_info.modifier & DOSAGE_ZOUT) {
-	    logprint("Error: --dosage 'Zout' modifier cannot be used with --score.\n");
+	    logerrprint("Error: --dosage 'Zout' modifier cannot be used with --score.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  if ((glm_modifier & GLM_STANDARD_BETA) || (dosage_info.modifier & DOSAGE_SEX)) {
-	    logprint("Error: --dosage + --score cannot be used with association analysis\nmodifiers/flags.\n");
+	    logerrprint("Error: --dosage + --score cannot be used with association analysis\nmodifiers/flags.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  dosage_info.modifier += (DOSAGE_SCORE - DOSAGE_GLM);
@@ -11341,38 +11404,38 @@ int32_t main(int32_t argc, char** argv) {
             score_info.modifier |= SCORE_HEADER;
 	  } else if (!strcmp(argv[cur_arg + uii], "sum")) {
 	    if (score_info.modifier & SCORE_NO_MEAN_IMPUTATION) {
-	      logprint("Error: --score 'sum' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
+	      logerrprint("Error: --score 'sum' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    } else if (dosage_info.modifier & DOSAGE_SCORE_NOSUM) {
-	      logprint("Error: --score 'sum' and 'no-sum' modifiers conflict.\n");
+	      logerrprint("Error: --score 'sum' and 'no-sum' modifiers conflict.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    score_info.modifier |= SCORE_SUM;
 	  } else if (!strcmp(argv[cur_arg + uii], "no-mean-imputation")) {
 	    if (score_info.modifier & SCORE_SUM) {
-	      logprint("Error: --score 'sum' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
+	      logerrprint("Error: --score 'sum' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    } else if (score_info.modifier & SCORE_CENTER) {
-	      logprint("Error: --score 'center' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
+	      logerrprint("Error: --score 'center' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
             score_info.modifier |= SCORE_NO_MEAN_IMPUTATION;
 	  } else if (!strcmp(argv[cur_arg + uii], "center")) {
 	    if (score_info.modifier & SCORE_NO_MEAN_IMPUTATION) {
-	      logprint("Error: --score 'center' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
+	      logerrprint("Error: --score 'center' and 'no-mean-imputation' modifiers cannot be used\ntogether.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
             score_info.modifier |= SCORE_CENTER;
 	  } else if (!strcmp(argv[cur_arg + uii], "no-sum")) {
 	    if (score_info.modifier & SCORE_SUM) {
-	      logprint("Error: --score 'sum' and 'no-sum' modifiers conflict.\n");
+	      logerrprint("Error: --score 'sum' and 'no-sum' modifiers conflict.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    dosage_info.modifier |= DOSAGE_SCORE_NOSUM;
 	  } else if (!strcmp(argv[cur_arg + uii], "include-cnt")) {
             dosage_info.modifier |= DOSAGE_SCORE_CNT;
 	  } else if (ujj == 3) {
-            logprint("Error: --score takes at most three numeric parameters.\n");
+            logerrprint("Error: --score takes at most three numeric parameters.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  } else {
 	    if (scan_posint_capped(argv[cur_arg + uii], (uint32_t*)&ii, (MAXLINEBUFLEN / 2) / 10, (MAXLINEBUFLEN / 2) % 10)) {
@@ -11385,17 +11448,17 @@ int32_t main(int32_t argc, char** argv) {
 	      score_info.effect_col = ii + 2;
 	    } else if (ujj == 1) {
 	      if ((uint32_t)ii == score_info.varid_col) {
-	        logprint("Error: --score variant ID and allele column numbers cannot match.\n");
+	        logerrprint("Error: --score variant ID and allele column numbers cannot match.\n");
 	        goto main_ret_INVALID_CMDLINE_A;
 	      }
               score_info.allele_col = ii;
               score_info.effect_col = ii + 1;
 	    } else {
 	      if ((uint32_t)ii == score_info.varid_col) {
-	        logprint("Error: --score variant ID and effect column numbers cannot match.\n");
+	        logerrprint("Error: --score variant ID and effect column numbers cannot match.\n");
 	        goto main_ret_INVALID_CMDLINE_A;
 	      } else if ((uint32_t)ii == score_info.allele_col) {
-	        logprint("Error: --score allele and effect column numbers cannot match.\n");
+	        logerrprint("Error: --score allele and effect column numbers cannot match.\n");
 	        goto main_ret_INVALID_CMDLINE_A;
 	      }
               score_info.effect_col = ii;
@@ -11406,10 +11469,10 @@ int32_t main(int32_t argc, char** argv) {
         calculation_type |= CALC_SCORE;
       } else if (!memcmp(argptr2, "core-no-mean-imputation", 24)) {
 	if (!(calculation_type & CALC_SCORE)) {
-	  logprint("Error: --score-no-mean-imputation must be used with --score.\n");
+	  logerrprint("Error: --score-no-mean-imputation must be used with --score.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (score_info.modifier & SCORE_CENTER) {
-	  logprint("Error: --score-no-mean-imputation cannot be used with --score 'center'\nmodifier.\n");
+	  logerrprint("Error: --score-no-mean-imputation cannot be used with --score 'center'\nmodifier.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --score-no-mean-imputation flag deprecated.  Use e.g.\n'--score ... no-mean-imputation'.\n");
@@ -11417,7 +11480,7 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "et-me-missing", 14)) {
 	if (load_rare & LOAD_RARE_CNV) {
-	  logprint("Error: --set-me-missing cannot be used with a .cnv fileset.\n");
+	  logerrprint("Error: --set-me-missing cannot be used with a .cnv fileset.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	misc_flags |= MISC_SET_ME_MISSING;
@@ -11435,17 +11498,17 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	} else {
           if (ld_info.modifier & LD_SHOW_TAGS_LIST_ALL) {
-	    logprint("Error: --list-all cannot be used with '--show-tags all'.\n");
+	    logerrprint("Error: --list-all cannot be used with '--show-tags all'.\n");
             goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  ld_info.modifier |= LD_SHOW_TAGS_LIST_ALL;
 	}
         calculation_type |= CALC_SHOW_TAGS;
       } else if (!memcmp(argptr2, "egment", 7)) {
-	logprint("Error: --segment flag retired.  Use another package, such as BEAGLE or\nGERMLINE, for this analysis.\n");
+	logerrprint("Error: --segment flag retired.  Use another package, such as BEAGLE or\nGERMLINE, for this analysis.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (!memcmp(argptr2, "kato", 5)) {
-	logprint("Error: --skato is not implemented yet.  Use e.g. PLINK/SEQ to perform this test\nfor now.\n");
+	logerrprint("Error: --skato is not implemented yet.  Use e.g. PLINK/SEQ to perform this test\nfor now.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto main_ret_1;
       } else if (memcmp(argptr2, "ilent", 6)) {
@@ -11459,7 +11522,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (makepheno_str) {
-	  logprint("Error: --tail-pheno cannot be used with --make-pheno.\n");
+	  logerrprint("Error: --tail-pheno cannot be used with --make-pheno.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (scan_double(argv[cur_arg + 1], &tail_bottom)) {
@@ -11475,7 +11538,7 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	}
 	if (tail_bottom > tail_top) {
-	  logprint("Error: Ltop cannot be larger than Hbottom for --tail-pheno.\n");
+	  logerrprint("Error: Ltop cannot be larger than Hbottom for --tail-pheno.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         filter_flags |= FILTER_FAM_REQ | FILTER_TAIL_PHENO;
@@ -11494,7 +11557,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "ab", 3)) {
 	logprint("Note: --tab flag deprecated.  Use '--recode tab ...'.\n");
 	if (recode_modifier & RECODE_DELIMX) {
-	  logprint("Error: Multiple --recode delimiter modifiers.\n");
+	  logerrprint("Error: Multiple --recode delimiter modifiers.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	recode_modifier |= RECODE_TAB;
@@ -11502,7 +11565,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "ranspose", 9)) {
 	logprint("Note: --transpose flag deprecated.  Use '--recode transpose ...'.\n");
 	if (recode_modifier & RECODE_LGEN) {
-	  logprint("Error: --recode 'transpose' and 'lgen' modifiers cannot be used together.\n");
+	  logerrprint("Error: --recode 'transpose' and 'lgen' modifiers cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	recode_modifier |= RECODE_TRANSPOSE;
@@ -11516,7 +11579,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	jj = strlen(argv[cur_arg + 1]);
 	if (jj > FNAMESIZE - 1) {
-	  logprint("Error: --tfam filename prefix too long.\n");
+	  logerrprint("Error: --tfam filename prefix too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(famname, argv[cur_arg + 1], jj + 1);
@@ -11531,7 +11594,7 @@ int32_t main(int32_t argc, char** argv) {
 	if (param_ct) {
 	  jj = strlen(argv[cur_arg + 1]);
 	  if (jj > FNAMESIZE - 6) {
-	    logprint("Error: --tfile filename prefix too long.\n");
+	    logerrprint("Error: --tfile filename prefix too long.\n");
 	    goto main_ret_OPEN_FAIL;
 	  }
 	  memcpy(memcpya(pedname, argv[cur_arg + 1], jj), ".tped", 6);
@@ -11550,7 +11613,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_INPUT_CONFLICT;
 	}
 	if (!(load_rare & (LOAD_RARE_TRANSPOSE | LOAD_RARE_TFAM))) {
-	  logprint("Error: --tped must be used with --tfam or --tfile.\n");
+	  logerrprint("Error: --tped must be used with --tfam or --tfile.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11558,20 +11621,20 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	jj = strlen(argv[cur_arg + 1]);
 	if (jj > FNAMESIZE - 1) {
-	  logprint("Error: --tped filename prefix too long.\n");
+	  logerrprint("Error: --tped filename prefix too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(pedname, argv[cur_arg + 1], jj + 1);
 	load_rare |= LOAD_RARE_TPED;
       } else if (!memcmp(argptr2, "o", 2)) {
 	if ((!all_words_zero(chrom_info.chrom_mask, CHROM_MASK_INITIAL_WORDS)) || chrom_info.incl_excl_name_stack) {
-	  logprint("Error: --to cannot be used with --autosome{-xy} or --{not-}chr.\n");
+	  logerrprint("Error: --to cannot be used with --autosome{-xy} or --{not-}chr.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (markername_snp) {
-	  logprint("Error: --to cannot be used with --snp.\n");
+	  logerrprint("Error: --to cannot be used with --snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (snps_range_list.names) {
-	  logprint("Error: --to cannot be used with --snps.\n");
+	  logerrprint("Error: --to cannot be used with --snps.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11583,13 +11646,13 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV;
       } else if ((!memcmp(argptr2, "o-bp", 5)) || (!memcmp(argptr2, "o-kb", 5)) || (!memcmp(argptr2, "o-mb", 5))) {
 	if (markername_snp && (!(filter_flags & FILTER_EXCLUDE_MARKERNAME_SNP))) {
-	  logprint("Error: --to-bp/-kb/-mb cannot be used with --snp.\n");
+	  logerrprint("Error: --to-bp/-kb/-mb cannot be used with --snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (snps_range_list.names) {
-	  logprint("Error: --to-bp/-kb/-mb cannot be used with --snps.\n");
+	  logerrprint("Error: --to-bp/-kb/-mb cannot be used with --snps.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (markername_to) {
-	  logprint("Error: --to-bp/-kb/-mb cannot be used with --to.\n");
+	  logerrprint("Error: --to-bp/-kb/-mb cannot be used with --to.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((!markername_from) && (marker_pos_start == -1)) {
@@ -11606,7 +11669,7 @@ int32_t main(int32_t argc, char** argv) {
 	  }
 	} else {
 	  if (marker_pos_end != -1) {
-	    logprint("Error: Multiple --to-bp/-kb/-mb values.\n");
+	    logerrprint("Error: Multiple --to-bp/-kb/-mb values.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  if (scan_double(argv[cur_arg + 1], &dxx)) {
@@ -11631,14 +11694,14 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP;
       } else if (!memcmp(argptr2, "rend", 5)) {
 	if (model_modifier & MODEL_ASSOC) {
-	  logprint("Error: --trend cannot be used with --assoc.\n");
+	  logerrprint("Error: --trend cannot be used with --assoc.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (model_modifier & MODEL_FISHER) {
-	  logprint("Error: --trend cannot be used with --model fisher.\n");
+	  logerrprint("Error: --trend cannot be used with --model fisher.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (model_modifier & (MODEL_PDOM | MODEL_PREC | MODEL_PGEN)) {
-	  logprint("Error: --trend cannot be used with --model dom/rec/gen.\n");
+	  logerrprint("Error: --trend cannot be used with --model dom/rec/gen.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --trend flag deprecated.  Use '--model trend-only ...'.\n");
@@ -11654,21 +11717,21 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_WWA;
 	}
         if (thin_keep_prob < (0.5 / 4294967296.0)) {
-	  logprint("Error: --thin variant retention probability too small.\n");
+	  logerrprint("Error: --thin variant retention probability too small.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (thin_keep_prob >= (4294967295.5 / 4294967296.0)) {
 	  if (scan_uint_defcap(argv[cur_arg + 1], &uii)) {
-	    logprint("Error: --thin variant retention probability too large.\n");
+	    logerrprint("Error: --thin variant retention probability too large.\n");
 	  } else {
 	    // VCFtools --thin = --bp-space...
-	    logprint("Error: --thin variant retention probability too large.  (Did you mean\n--bp-space?)\n");
+	    logerrprint("Error: --thin variant retention probability too large.  (Did you mean\n--bp-space?)\n");
 	  }
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	filter_flags |= FILTER_BIM_REQ | FILTER_DOSAGEMAP | FILTER_NOCNV;
       } else if (!memcmp(argptr2, "hin-count", 10)) {
 	if (thin_keep_prob != 1.0) {
-	  logprint("Error: --thin cannot be used with --thin-count.\n");
+	  logerrprint("Error: --thin cannot be used with --thin-count.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11689,16 +11752,16 @@ int32_t main(int32_t argc, char** argv) {
           goto main_ret_INVALID_CMDLINE_WWA;
         }
         if (thin_keep_sample_prob < (0.5 / 4294967296.0)) {
-          LOGPRINTF("Error: --thin-indiv %s retention probability too small.\n", g_species_singular);
+          LOGERRPRINTF("Error: --thin-indiv %s retention probability too small.\n", g_species_singular);
           goto main_ret_INVALID_CMDLINE_A;
         } else if (thin_keep_sample_prob >= (4294967295.5 / 4294967296.0)) {
-          LOGPRINTF("Error: --thin-indiv %s retention probability too large.\n", g_species_singular);
+          LOGERRPRINTF("Error: --thin-indiv %s retention probability too large.\n", g_species_singular);
           goto main_ret_INVALID_CMDLINE_A;
         }
       } else if (!memcmp(argptr2, "hin-indiv-count", 16)) {
 	UNSTABLE("thin-indiv-count");
         if (thin_keep_sample_prob != 1.0) {
-          logprint("Error: --thin-indiv cannot be used with --thin-indiv-count.\n");
+          logerrprint("Error: --thin-indiv cannot be used with --thin-indiv-count.\n");
           goto main_ret_INVALID_CMDLINE_WWA;
         }
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11710,14 +11773,14 @@ int32_t main(int32_t argc, char** argv) {
         }
       } else if (!memcmp(argptr2, "ests", 5)) {
 	if (!(calculation_type & CALC_GLM)) {
-	  logprint("Error: --tests must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --tests must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((param_ct == 1) && (!strcmp(argv[cur_arg + 1], "all"))) {
 	  glm_modifier |= GLM_TEST_ALL;
 	} else {
 	  if (glm_modifier & GLM_TEST_ALL) {
-	    logprint("Error: --test-all cannot be used with --tests.\n");
+	    logerrprint("Error: --test-all cannot be used with --tests.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  retval = parse_name_ranges(param_ct, '-', &(argv[cur_arg]), &tests_range_list, 1);
@@ -11727,7 +11790,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "est-all", 8)) {
 	if (!(calculation_type & CALC_GLM)) {
-	  logprint("Error: --test-all must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --test-all must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --test-all flag deprecated.  Use '--tests all'.\n");
@@ -11744,7 +11807,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_NOMEM;
 	}
 	if (!strcmp(epi_info.twolocus_mkr1, epi_info.twolocus_mkr2)) {
-          logprint("Error: --twolocus parameters cannot match.\n");
+          logerrprint("Error: --twolocus parameters cannot match.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         calculation_type |= CALC_EPI;
@@ -11755,17 +11818,17 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
           if (!strcmp(argv[cur_arg + uii], "perm")) {
             if (testmiss_modifier & TESTMISS_MPERM) {
-              logprint("Error: --test-missing 'mperm' and 'perm' cannot be used together.\n");
+              logerrprint("Error: --test-missing 'mperm' and 'perm' cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
             testmiss_modifier |= TESTMISS_PERM;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
             if (testmiss_modifier & TESTMISS_PERM) {
-              logprint("Error: --test-missing 'mperm' and 'perm' cannot be used together.\n");
+              logerrprint("Error: --test-missing 'mperm' and 'perm' cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    } else if (testmiss_modifier & TESTMISS_MPERM) {
 	      // when --mperm and --test-missing mperm=[] are used together
-              logprint("Error: Duplicate --test-missing 'mperm' modifier.\n");
+              logerrprint("Error: Duplicate --test-missing 'mperm' modifier.\n");
               goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &testmiss_mperm_val)) {
@@ -11778,7 +11841,7 @@ int32_t main(int32_t argc, char** argv) {
 	  } else if (!strcmp(argv[cur_arg + uii], "midp")) {
             testmiss_modifier |= TESTMISS_MIDP;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-            logprint("Error: Improper --test-missing mperm syntax.  (Use '--test-missing\nmperm=[value]'.)\n");
+            logerrprint("Error: Improper --test-missing mperm syntax.  (Use '--test-missing\nmperm=[value]'.)\n");
             goto main_ret_INVALID_CMDLINE;
 	  } else {
             sprintf(logbuf, "Error: Invalid --test-missing parameter '%s'.\n", argv[cur_arg + uii]);
@@ -11796,31 +11859,31 @@ int32_t main(int32_t argc, char** argv) {
 	for (uii = 1; uii <= param_ct; uii++) {
 	  if (!strcmp(argv[cur_arg + uii], "exact")) {
 	    if (family_info.tdt_modifier & TDT_MIDP) {
-	      logprint("Error: --tdt 'exact' modifier cannot be used with 'exact-midp'.\n");
+	      logerrprint("Error: --tdt 'exact' modifier cannot be used with 'exact-midp'.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (family_info.tdt_modifier & TDT_POO) {
-	      logprint("Error: --tdt parent-of-origin analysis does not currently support exact tests.\n");
+	      logerrprint("Error: --tdt parent-of-origin analysis does not currently support exact tests.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_EXACT;
 	  } else if (!strcmp(argv[cur_arg + uii], "exact-midp")) {
 	    if ((family_info.tdt_modifier & (TDT_EXACT | TDT_MIDP)) == TDT_EXACT) {
-	      logprint("Error: --tdt 'exact' modifier cannot be used with 'exact-midp'.\n");
+	      logerrprint("Error: --tdt 'exact' modifier cannot be used with 'exact-midp'.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    } else if (family_info.tdt_modifier & TDT_POO) {
-	      logprint("Error: --tdt parent-of-origin analysis does not currently support exact tests.\n");
+	      logerrprint("Error: --tdt parent-of-origin analysis does not currently support exact tests.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_EXACT | TDT_MIDP;
 	  } else if (!strcmp(argv[cur_arg + uii], "poo")) {
 	    if (family_info.tdt_modifier & TDT_EXACT) {
-	      logprint("Error: --tdt parent-of-origin analysis does not currently support exact tests.\n");
+	      logerrprint("Error: --tdt parent-of-origin analysis does not currently support exact tests.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_POO;
 	  } else if (!strcmp(argv[cur_arg + uii], "perm")) {
 	    if (family_info.tdt_modifier & TDT_MPERM) {
-	      logprint("Error: --tdt 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --tdt 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_PERM;
@@ -11828,10 +11891,10 @@ int32_t main(int32_t argc, char** argv) {
 	    family_info.tdt_modifier |= TDT_PERM_COUNT;
 	  } else if ((strlen(argv[cur_arg + uii]) > 6) && (!memcmp(argv[cur_arg + uii], "mperm=", 6))) {
 	    if (family_info.tdt_modifier & TDT_PERM) {
-	      logprint("Error: --tdt 'mperm' and 'perm' cannot be used together.\n");
+	      logerrprint("Error: --tdt 'mperm' and 'perm' cannot be used together.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    } else if (family_info.tdt_modifier & TDT_MPERM) {
-	      logprint("Error: Duplicate --tdt 'mperm' modifier.\n");
+	      logerrprint("Error: Duplicate --tdt 'mperm' modifier.\n");
 	      goto main_ret_INVALID_CMDLINE;
 	    }
 	    if (scan_posint_defcap(&(argv[cur_arg + uii][6]), &family_info.tdt_mperm_val)) {
@@ -11841,32 +11904,32 @@ int32_t main(int32_t argc, char** argv) {
             family_info.tdt_modifier |= TDT_MPERM;
 	  } else if (!strcmp(argv[cur_arg + uii], "parentdt1")) {
 	    if (family_info.tdt_modifier & TDT_PARENPERM2) {
-	      logprint("Error: --tdt 'parentdt1' and 'parentdt2' modifiers cannot be used together.\n");
+	      logerrprint("Error: --tdt 'parentdt1' and 'parentdt2' modifiers cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_PARENPERM1;
 	  } else if (!strcmp(argv[cur_arg + uii], "parentdt2")) {
 	    if (family_info.tdt_modifier & TDT_PARENPERM1) {
-	      logprint("Error: --tdt 'parentdt1' and 'parentdt2' modifiers cannot be used together.\n");
+	      logerrprint("Error: --tdt 'parentdt1' and 'parentdt2' modifiers cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_PARENPERM2;
 	  } else if (!strcmp(argv[cur_arg + uii], "pat")) {
 	    if (family_info.tdt_modifier & TDT_POOPERM_MAT) {
-	      logprint("Error: --tdt 'pat' and 'mat' modifiers cannot be used together.\n");
+	      logerrprint("Error: --tdt 'pat' and 'mat' modifiers cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_POOPERM_PAT;
 	  } else if (!strcmp(argv[cur_arg + uii], "parentdt2")) {
 	    if (family_info.tdt_modifier & TDT_POOPERM_PAT) {
-	      logprint("Error: --tdt 'pat' and 'mat' modifiers cannot be used together.\n");
+	      logerrprint("Error: --tdt 'pat' and 'mat' modifiers cannot be used together.\n");
               goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    family_info.tdt_modifier |= TDT_POOPERM_MAT;
 	  } else if (!strcmp(argv[cur_arg + uii], "set-test")) {
             family_info.tdt_modifier |= TDT_SET_TEST;
 	  } else if (!strcmp(argv[cur_arg + uii], "mperm")) {
-	    logprint("Error: Improper --tdt mperm syntax.  (Use '--tdt mperm=[value]'.)\n");
+	    logerrprint("Error: Improper --tdt mperm syntax.  (Use '--tdt mperm=[value]'.)\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  } else {
 	    sprintf(logbuf, "Error: Invalid --tdt parameter '%s'.\n", argv[cur_arg + uii]);
@@ -11875,28 +11938,28 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	if (!(family_info.tdt_modifier & TDT_POO)) {
 	  if (family_info.tdt_modifier & TDT_PERM) {
-	    logprint("Error: Regular --tdt analysis no longer supports adaptive permutation.  Use an\nexact test instead.\n");
+	    logerrprint("Error: Regular --tdt analysis no longer supports adaptive permutation.  Use an\nexact test instead.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if (family_info.tdt_modifier & (TDT_POOPERM_PAT | TDT_POOPERM_MAT)) {
-	    logprint("Error: --tdt 'pat'/'mat' must be used with parent-of-origin analysis.\n");
+	    logerrprint("Error: --tdt 'pat'/'mat' must be used with parent-of-origin analysis.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if ((family_info.tdt_modifier & (TDT_PARENPERM1 | TDT_PARENPERM2 | TDT_SET_TEST)) && (!(family_info.tdt_modifier & TDT_MPERM))) {
-	    logprint("Error: --tdt 'parentdt1'/'parentdt2'/'set-test' requires permutation.\n");
+	    logerrprint("Error: --tdt 'parentdt1'/'parentdt2'/'set-test' requires permutation.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	} else {
 	  if (family_info.tdt_modifier & (TDT_PARENPERM1 | TDT_PARENPERM2)) {
-	    logprint("Error: --tdt 'parentdt1'/'parentdt2' cannot be used with parent-of-origin\nanalysis.\n");
+	    logerrprint("Error: --tdt 'parentdt1'/'parentdt2' cannot be used with parent-of-origin\nanalysis.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if ((family_info.tdt_modifier & (TDT_POOPERM_PAT | TDT_POOPERM_MAT | TDT_SET_TEST)) && (!(family_info.tdt_modifier & (TDT_PERM | TDT_MPERM)))) {
-	    logprint("Error: --tdt poo 'pat'/'mat'/'set-test' requires permutation.\n");
+	    logerrprint("Error: --tdt poo 'pat'/'mat'/'set-test' requires permutation.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	}
 	calculation_type |= CALC_TDT;
       } else if (!memcmp(argptr2, "ag-kb", 6)) {
         if (!(calculation_type & CALC_SHOW_TAGS)) {
-	  logprint("Error: --tag-kb must be used with --show-tags.\n");
+	  logerrprint("Error: --tag-kb must be used with --show-tags.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11913,7 +11976,7 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "ag-r2", 6)) {
         if (!(calculation_type & CALC_SHOW_TAGS)) {
-	  logprint("Error: --tag-r2 must be used with --show-tags.\n");
+	  logerrprint("Error: --tag-r2 must be used with --show-tags.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -11926,10 +11989,10 @@ int32_t main(int32_t argc, char** argv) {
 	ld_info.show_tags_r2 = dxx;
       } else if (!memcmp(argptr2, "ag-mode2", 8)) {
         if (!(calculation_type & CALC_SHOW_TAGS)) {
-	  logprint("Error: --tag-mode2 must be used with --show-tags.\n");
+	  logerrprint("Error: --tag-mode2 must be used with --show-tags.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (!(ld_info.show_tags_fname)) {
-	  logprint("Error: --tag-mode2 cannot be used with '--show-tags all'.\n");
+	  logerrprint("Error: --tag-mode2 cannot be used with '--show-tags all'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	ld_info.modifier |= LD_SHOW_TAGS_MODE2;
@@ -11942,26 +12005,26 @@ int32_t main(int32_t argc, char** argv) {
     case 'u':
       if (!memcmp(argptr2, "nrelated-heritability", 22)) {
 #ifdef NOLAPACK
-        logprint("Error: --unrelated-heritability requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
+        logerrprint("Error: --unrelated-heritability requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
 	goto main_ret_INVALID_CMDLINE;
 #else
 	UNSTABLE("unrelated-heritability");
 	if (rel_info.modifier & REL_CALC_COV) {
-	  logprint("Error: --unrelated-heritability flag cannot coexist with a covariance\nmatrix calculation.\n");
+	  logerrprint("Error: --unrelated-heritability flag cannot coexist with a covariance\nmatrix calculation.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (parallel_tot > 1) {
-	  logprint("Error: --parallel and --unrelated-heritability cannot be used together.\n");
+	  logerrprint("Error: --parallel and --unrelated-heritability cannot be used together.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (calculation_type & CALC_PCA) {
-	  logprint("Error: --pca cannot be used with --unrelated-heritability.\n");
+	  logerrprint("Error: --pca cannot be used with --unrelated-heritability.\n");
           goto main_ret_INVALID_CMDLINE;
 	}
 	if (load_rare & (LOAD_RARE_GRM | LOAD_RARE_GRM_BIN)) {
 	  if (calculation_type & CALC_REL_CUTOFF) {
-	    logprint("Error: --unrelated-heritability + --grm-gz/--grm-bin cannot be used with\n--rel-cutoff.\n");
+	    logerrprint("Error: --unrelated-heritability + --grm-gz/--grm-bin cannot be used with\n--rel-cutoff.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  } else if (!phenoname) {
-	    logprint("Error: --unrelated-heritability + --grm-gz/--grm-bin requires --pheno as well.\n");
+	    logerrprint("Error: --unrelated-heritability + --grm-gz/--grm-bin requires --pheno as well.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	}
@@ -12040,15 +12103,15 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "pdate-cm", 9)) {
 	if (cnv_calc_type & CNV_MAKE_MAP) {
-	  logprint("Error: --update-cm cannot be used with --cnv-make-map.\n");
+	  logerrprint("Error: --update-cm cannot be used with --cnv-make-map.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (cm_map_fname) {
-	  logprint("Error: --update-cm cannot be used with --cm-map.\n");
+	  logerrprint("Error: --update-cm cannot be used with --cm-map.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (update_map_modifier) {
-	  logprint("Error: --update-map 'cm' modifier cannot be used with 'chr'.\n");
+	  logerrprint("Error: --update-map 'cm' modifier cannot be used with 'chr'.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 4)) {
@@ -12104,10 +12167,10 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (update_chr) {
-	  logprint("Error: --update-name cannot be used with --update-chr.\n");
+	  logerrprint("Error: --update-name cannot be used with --update-chr.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	} else if (update_cm) {
-	  logprint("Error: --update-name cannot be used with --update-cm.\n");
+	  logerrprint("Error: --update-name cannot be used with --update-cm.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 4)) {
@@ -12115,11 +12178,11 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	if (!param_ct) {
 	  if (!update_map) {
-	    logprint("Error: Deprecated parameter-free --update-name cannot be used without\n--update-map.\n");
+	    logerrprint("Error: Deprecated parameter-free --update-name cannot be used without\n--update-map.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
 	  if ((update_map->colx != 2) || (update_map->colid != 1) || (update_map->skip) || (update_map->skipchar)) {
-	    logprint("Error: Multi-parameter --update-map cannot be used with deprecated\nparameter-free --update-name.\n");
+	    logerrprint("Error: Multi-parameter --update-map cannot be used with deprecated\nparameter-free --update-name.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  logprint("Note: --update-map [filename] + parameter-free --update-name deprecated.  Use\n--update-name [filename] instead.\n");
@@ -12129,7 +12192,7 @@ int32_t main(int32_t argc, char** argv) {
 	  if (update_map) {
 	    // no point in explaining the deprecated exception to this in the
 	    // error message
-	    logprint("Error: --update-name cannot be used with --update-map.\n");
+	    logerrprint("Error: --update-name cannot be used with --update-map.\n");
 	    goto main_ret_INVALID_CMDLINE;
 	  }
 	  retval = alloc_2col(&update_name, &(argv[cur_arg + 1]), argptr, param_ct);
@@ -12143,7 +12206,7 @@ int32_t main(int32_t argc, char** argv) {
 	  goto main_ret_INVALID_CMDLINE_2A;
 	}
 	if (update_ids_fname) {
-	  logprint("Error: --update-parents cannot be used with --update-ids.\n");
+	  logerrprint("Error: --update-parents cannot be used with --update-ids.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	retval = alloc_fname(&update_parents_fname, argv[cur_arg + 1], argptr, 0);
@@ -12153,7 +12216,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_FAM_REQ;
       } else if (!memcmp(argptr2, "pdate-sex", 10)) {
 	if (update_ids_fname) {
-	  logprint("Error: --update-sex cannot be used with --update-ids.\n");
+	  logerrprint("Error: --update-sex cannot be used with --update-ids.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -12172,7 +12235,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_FAM_REQ;
       } else if (!memcmp(argptr2, "nbounded", 9)) {
 	if (!(calculation_type & CALC_GENOME)) {
-	  logprint("Error: --unbounded must be used with --genome.\n");
+	  logerrprint("Error: --unbounded must be used with --genome.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	logprint("Note: --unbounded flag deprecated.  Use '--genome unbounded'.\n");
@@ -12186,7 +12249,7 @@ int32_t main(int32_t argc, char** argv) {
     case 'v':
       if (!memcmp(argptr2, "if", 3)) {
 	if (((!(calculation_type & CALC_GLM)) || (glm_modifier & GLM_LOGISTIC)) && (!((calculation_type & CALC_EPI) || (!(epi_info.modifier & EPI_REG))))) {
-	  logprint("Error: --vif must be used with --linear/--epistasis.\n");
+	  logerrprint("Error: --vif must be used with --linear/--epistasis.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12202,10 +12265,10 @@ int32_t main(int32_t argc, char** argv) {
 	}
       } else if (!memcmp(argptr2, "egas", 5)) {
 	if (!set_info.fname) {
-	  logprint("Error: --vegas must be used with --set.\n");
+	  logerrprint("Error: --vegas must be used with --set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
-	logprint("Error: --vegas is currently under development.\n");
+	logerrprint("Error: --vegas is currently under development.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto main_ret_1;
 	goto main_param_zero;
@@ -12218,14 +12281,14 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	uii = strlen(argv[cur_arg + 1]);
 	if (uii > FNAMESIZE - 1) {
-	  logprint("Error: --vcf filename too long.\n");
+	  logerrprint("Error: --vcf filename too long.\n");
 	  goto main_ret_OPEN_FAIL;
 	}
 	memcpy(pedname, argv[cur_arg + 1], uii + 1);
 	load_rare = LOAD_RARE_VCF;
       } else if (!memcmp(argptr2, "cf-min-qual", 12)) {
 	if (!(load_rare & (LOAD_RARE_VCF | LOAD_RARE_BCF))) {
-	  logprint("Error: --vcf-min-qual must be used with --vcf/--bcf.\n");
+	  logerrprint("Error: --vcf-min-qual must be used with --vcf/--bcf.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12238,7 +12301,7 @@ int32_t main(int32_t argc, char** argv) {
 	vcf_min_qual *= 1 - SMALL_EPSILON;
       } else if (!memcmp(argptr2, "cf-filter", 10)) {
 	if (!(load_rare & (LOAD_RARE_VCF | LOAD_RARE_BCF))) {
-	  logprint("Error: --vcf-filter must be used with --vcf/--bcf.\n");
+	  logerrprint("Error: --vcf-filter must be used with --vcf/--bcf.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (param_ct) {
@@ -12251,7 +12314,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "cf-min-gp", 10)) {
 	if (!(load_rare & LOAD_RARE_VCF)) {
 	  // er, probably want to support BCF too
-	  logprint("Error: --vcf-min-gp must be used with --vcf.\n");
+	  logerrprint("Error: --vcf-min-gp must be used with --vcf.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12265,7 +12328,7 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "cf-min-gq", 10)) {
 	if (!(load_rare & LOAD_RARE_VCF)) {
 	  // probably want to support BCF too
-	  logprint("Error: --vcf-min-gq must be used with --vcf.\n");
+	  logerrprint("Error: --vcf-min-gq must be used with --vcf.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12278,11 +12341,11 @@ int32_t main(int32_t argc, char** argv) {
 	vcf_min_gq *= 1 - SMALL_EPSILON;
       } else if (!memcmp(argptr2, "cf-idspace-to", 14)) {
 	if (!(load_rare & (LOAD_RARE_VCF | LOAD_RARE_BCF))) {
-	  logprint("Error: --vcf-idspace-to must be used with --vcf/--bcf.\n");
+	  logerrprint("Error: --vcf-idspace-to must be used with --vcf/--bcf.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (id_delim == ' ') {
-	  logprint("Error: --vcf-idspace-to cannot be used when the --id-delim character is space.\n");
+	  logerrprint("Error: --vcf-idspace-to cannot be used when the --id-delim character is space.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12290,15 +12353,15 @@ int32_t main(int32_t argc, char** argv) {
 	}
 	vcf_idspace_to = extract_char_param(argv[cur_arg + 1]);
 	if (!vcf_idspace_to) {
-	  logprint("Error: --vcf-idspace-to parameter must be a single character.\n");
+	  logerrprint("Error: --vcf-idspace-to parameter must be a single character.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (((unsigned char)vcf_idspace_to) <= ' ') {
-	  logprint("Error: --vcf-idspace-to parameter must be a nonspace character.\n");
+	  logerrprint("Error: --vcf-idspace-to parameter must be a nonspace character.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
       } else if (!memcmp(argptr2, "cf-half-call", 13)) {
         if (!(load_rare & LOAD_RARE_VCF)) {
-	  logprint("Error: --vcf-half-call must be used with --vcf.\n");
+	  logerrprint("Error: --vcf-half-call must be used with --vcf.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
         if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12334,7 +12397,7 @@ int32_t main(int32_t argc, char** argv) {
 	calculation_type |= CALC_WRITE_VAR_RANGES;
       } else if (!memcmp(argptr2, "indow", 6)) {
         if (!markername_snp) {
-	  logprint("Error: --window must be used with --snp or --exclude-snp.\n");
+	  logerrprint("Error: --window must be used with --snp or --exclude-snp.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12353,15 +12416,15 @@ int32_t main(int32_t argc, char** argv) {
 	// no need to set filter_flags due to --snp/--exclude-snp req.
       } else if (!memcmp(argptr2, "ithin", 6)) {
         if (loop_assoc_fname) {
-	  logprint("Error: --within cannot be used with --loop-assoc.\n");
+	  logerrprint("Error: --within cannot be used with --loop-assoc.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (misc_flags & MISC_FAMILY_CLUSTERS) {
-          logprint("Error: --within cannot be used with --family.\n");
+          logerrprint("Error: --within cannot be used with --family.\n");
           goto main_ret_INVALID_CMDLINE_A;
 	}
 	if ((calculation_type & CALC_FREQ) && (misc_flags & MISC_FREQ_COUNTS)) {
-	  logprint("Error: --within cannot be used with '--freq counts'.\n");
+	  logerrprint("Error: --within cannot be used with '--freq counts'.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 2)) {
@@ -12372,7 +12435,7 @@ int32_t main(int32_t argc, char** argv) {
 	  if ((strlen(argv[cur_arg + 1]) == 7) && (!memcmp(argv[cur_arg + 1], "keep-", 5)) && match_upper(&(argv[cur_arg + 1][5]), "NA")) {
 	    uii = 2;
 	  } else if ((strlen(argv[cur_arg + 2]) != 7) || memcmp(argv[cur_arg + 2], "keep-", 5) || (!match_upper(&(argv[cur_arg + 2][5]), "NA"))) {
-            logprint("Error: Invalid --within parameter sequence.\n");
+            logerrprint("Error: Invalid --within parameter sequence.\n");
 	    goto main_ret_INVALID_CMDLINE_A;
 	  }
           misc_flags |= MISC_LOAD_CLUSTER_KEEP_NA;
@@ -12384,7 +12447,7 @@ int32_t main(int32_t argc, char** argv) {
 	filter_flags |= FILTER_FAM_REQ;
       } else if (!memcmp(argptr2, "ith-phenotype", 14)) {
 	if (!covar_fname) {
-	  logprint("Error: --with-phenotype cannot be used without --covar.\n");
+	  logerrprint("Error: --with-phenotype cannot be used without --covar.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 2)) {
@@ -12395,13 +12458,13 @@ int32_t main(int32_t argc, char** argv) {
 	    write_covar_modifier |= WRITE_COVAR_NO_PARENTS;
 	  } else if (!strcmp(argv[cur_arg + uii], "no-sex")) {
 	    if (write_covar_modifier & WRITE_COVAR_FEMALE_2) {
-	      logprint("Error: --with-phenotype 'female-2' modifier cannot be used with 'no-sex'.\n");
+	      logerrprint("Error: --with-phenotype 'female-2' modifier cannot be used with 'no-sex'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    write_covar_modifier |= WRITE_COVAR_NO_SEX;
 	  } else if (!strcmp(argv[cur_arg + uii], "female-2")) {
 	    if (write_covar_modifier & WRITE_COVAR_NO_SEX) {
-	      logprint("Error: --with-phenotype 'female-2' modifier cannot be used with 'no-sex'.\n");
+	      logerrprint("Error: --with-phenotype 'female-2' modifier cannot be used with 'no-sex'.\n");
 	      goto main_ret_INVALID_CMDLINE_A;
 	    }
 	    write_covar_modifier |= WRITE_COVAR_FEMALE_2;
@@ -12413,7 +12476,7 @@ int32_t main(int32_t argc, char** argv) {
 	write_covar_modifier |= WRITE_COVAR_PHENO;
       } else if (!memcmp(argptr2, "ith-reference", 14)) {
 	if ((recode_modifier & RECODE_TYPEMASK) != RECODE_LGEN) {
-	  logprint("Error: --with-reference must be used with --recode lgen.\n");
+	  logerrprint("Error: --with-reference must be used with --recode lgen.\n");
 	  goto main_ret_INVALID_CMDLINE;
 	}
 	logprint("Note: --with-reference flag deprecated.  Use '--recode lgen-ref' instead.\n");
@@ -12421,18 +12484,18 @@ int32_t main(int32_t argc, char** argv) {
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "rite-covar", 11)) {
 	if (calculation_type & (CALC_MAKE_BED | CALC_MAKE_FAM | CALC_RECODE)) {
-	  logprint("Error: --write-covar cannot be used with --make-bed/--make-just-fam/--recode.\n");
+	  logerrprint("Error: --write-covar cannot be used with --make-bed/--make-just-fam/--recode.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (!covar_fname) {
-	  logprint("Error: --write-covar cannot be used without --covar.\n");
+	  logerrprint("Error: --write-covar cannot be used without --covar.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         calculation_type |= CALC_WRITE_COVAR;
 	goto main_param_zero;
       } else if (!memcmp(argptr2, "rite-cluster", 13)) {
 	if ((!cluster.fname) && (!(misc_flags & MISC_FAMILY_CLUSTERS))) {
-	  logprint("Error: --write-cluster must be used with --within/--family.\n");
+	  logerrprint("Error: --write-cluster must be used with --within/--family.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 0, 1)) {
@@ -12448,7 +12511,7 @@ int32_t main(int32_t argc, char** argv) {
         calculation_type |= CALC_WRITE_CLUSTER;
       } else if (!memcmp(argptr2, "rite-set", 9)) {
 	if (!set_info.fname) {
-	  logprint("Error: --write-set must be used with --set/--make-set.\n");
+	  logerrprint("Error: --write-set must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         calculation_type |= CALC_WRITE_SET;
@@ -12456,7 +12519,7 @@ int32_t main(int32_t argc, char** argv) {
         goto main_param_zero;
       } else if (!memcmp(argptr2, "rite-set-r2", 11)) {
         if (!set_info.fname) {
-	  logprint("Error: --write-set-r2 must be used with --set/--make-set.\n");
+	  logerrprint("Error: --write-set-r2 must be used with --set/--make-set.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         set_info.modifier |= SET_R2_WRITE;
@@ -12464,7 +12527,7 @@ int32_t main(int32_t argc, char** argv) {
         goto main_param_zero;
       } else if (!memcmp(argptr2, "ith-freqs", 10)) {
 	if (!(calculation_type & CALC_LD)) {
-	  logprint("Error: --with-freqs must be used with --r/--r2.\n");
+	  logerrprint("Error: --with-freqs must be used with --r/--r2.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
         ld_info.modifier |= LD_WITH_FREQS;
@@ -12476,15 +12539,15 @@ int32_t main(int32_t argc, char** argv) {
       } else if (!memcmp(argptr2, "rite-dosage", 12)) {
 	if (!(dosage_info.modifier & DOSAGE_GLM)) {
 	  if (dosage_info.modifier & DOSAGE_OCCUR) {
-	    logprint("Error: --write-dosage cannot be used with '--dosage occur'.\n");
+	    logerrprint("Error: --write-dosage cannot be used with '--dosage occur'.\n");
 	  } else if (dosage_info.modifier & DOSAGE_SCORE) {
-	    logprint("Error: --write-dosage cannot be used with --score.\n");
+	    logerrprint("Error: --write-dosage cannot be used with --score.\n");
 	  } else {
-	    logprint("Error: --write-dosage must be used with --dosage.\n");
+	    logerrprint("Error: --write-dosage must be used with --dosage.\n");
 	  }
           goto main_ret_INVALID_CMDLINE_A;
 	} else if ((glm_modifier & GLM_STANDARD_BETA) || (dosage_info.modifier & DOSAGE_SEX)) {
-	  logprint("Error: --write-dosage cannot be used with --dosage association analysis flags.\n");
+	  logerrprint("Error: --write-dosage cannot be used with --dosage association analysis flags.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	dosage_info.modifier += (DOSAGE_WRITE - DOSAGE_GLM);
@@ -12499,7 +12562,7 @@ int32_t main(int32_t argc, char** argv) {
     case 'x':
       if (!memcmp(argptr2, "chr-model", 10)) {
 	if (!(calculation_type & CALC_GLM)) {
-	  logprint("Error: --xchr-model must be used with --linear or --logistic.\n");
+	  logerrprint("Error: --xchr-model must be used with --linear or --logistic.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if (glm_modifier & (GLM_GENOTYPIC | GLM_HETHOM | GLM_DOMINANT | GLM_RECESSIVE)) {
 	  sprintf(logbuf, "Error: --xchr-model cannot be used with --%s %s.\n", (glm_modifier & GLM_LOGISTIC)? "logistic" : "linear", (glm_modifier & GLM_GENOTYPIC)? "genotypic" : ((glm_modifier & GLM_HETHOM)? "hethom" : ((glm_modifier & GLM_DOMINANT)? "dominant" : "recessive")));
@@ -12521,11 +12584,11 @@ int32_t main(int32_t argc, char** argv) {
     case 'z':
       if (!memcmp(argptr2, "ero-cluster", 12)) {
 	if ((!cluster.fname) && (!(misc_flags & MISC_FAMILY_CLUSTERS))) {
-	  logprint("Error: --zero-cluster must be used with --within/--family.\n");
+	  logerrprint("Error: --zero-cluster must be used with --within/--family.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	} else if ((calculation_type != CALC_MAKE_BED) || (geno_thresh != 1.0) || (mind_thresh != 1.0) || (hwe_thresh != 0.0) || (min_maf != 0.0) || (max_maf != 0.5)) {
 	  // prevent old pipelines from silently breaking
-	  logprint("Error: --zero-cluster must now be used with --make-bed, no other output\ncommands, and no genotype-based filters.\n");
+	  logerrprint("Error: --zero-cluster must now be used with --make-bed, no other output\ncommands, and no genotype-based filters.\n");
 	  goto main_ret_INVALID_CMDLINE_A;
 	}
 	if (enforce_param_ct_range(param_ct, argv[cur_arg], 1, 1)) {
@@ -12562,64 +12625,64 @@ int32_t main(int32_t argc, char** argv) {
     if (load_rare & (LOAD_RARE_GRM | LOAD_RARE_GRM_BIN)) {
       if ((!(calculation_type & (CALC_REL_CUTOFF | CALC_UNRELATED_HERITABILITY))) || (calculation_type & (~(CALC_REL_CUTOFF | CALC_RELATIONSHIP | CALC_UNRELATED_HERITABILITY)))) {
 	if (load_rare == LOAD_RARE_GRM) {
-	  logprint("Error: --grm-gz currently must be used with --rel-cutoff (possibly combined\nwith --make-grm-gz/--make-grm-bin) or --unrelated-heritability.\n");
+	  logerrprint("Error: --grm-gz currently must be used with --rel-cutoff (possibly combined\nwith --make-grm-gz/--make-grm-bin) or --unrelated-heritability.\n");
 	} else {
-	  logprint("Error: --grm-bin currently must be used with --rel-cutoff (possibly combined\nwith --make-grm-gz/--make-grm-bin) or --unrelated-heritability.\n");
+	  logerrprint("Error: --grm-bin currently must be used with --rel-cutoff (possibly combined\nwith --make-grm-gz/--make-grm-bin) or --unrelated-heritability.\n");
 	}
 	goto main_ret_INVALID_CMDLINE_A;
       }
     }
     if (!mperm_val) {
       if ((cnv_calc_type & CNV_SAMPLE_PERM) && (!cnv_sample_mperms)) {
-	logprint("Error: --cnv-indiv-perm requires a permutation count.\n");
+	logerrprint("Error: --cnv-indiv-perm requires a permutation count.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       } else if ((cnv_calc_type & CNV_TEST_REGION) && (!cnv_test_region_mperms)) {
-	logprint("Error: --cnv-test-region requires a permutation count.\n");
+	logerrprint("Error: --cnv-test-region requires a permutation count.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       }
     }
   } else if ((load_params & LOAD_PARAMS_BFILE_ALL) && (load_params != LOAD_PARAMS_BFILE_ALL)) {
     if ((calculation_type & (~(CALC_ONLY_BIM | CALC_ONLY_FAM))) || (filter_flags & FILTER_ALL_REQ)) {
-      logprint("Error: A full .bed + .bim + .fam fileset is required for this.\n");
+      logerrprint("Error: A full .bed + .bim + .fam fileset is required for this.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
     if ((!mapname[0]) && ((calculation_type & (~CALC_ONLY_FAM)) || (filter_flags & FILTER_BIM_REQ))) {
-      logprint("Error: A .bim file is required for this.\n");
+      logerrprint("Error: A .bim file is required for this.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
     if ((!famname[0]) && ((calculation_type & (~CALC_ONLY_BIM)) || (filter_flags & FILTER_FAM_REQ))) {
-      logprint("Error: A .fam file is required for this.\n");
+      logerrprint("Error: A .fam file is required for this.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
   if (sample_sort && (calculation_type & (~(CALC_MERGE | CALC_MAKE_BED)))) {
-    logprint("Error: --indiv-sort only affects --make-bed and --merge/--bmerge/--merge-list.\n");
+    logerrprint("Error: --indiv-sort only affects --make-bed and --merge/--bmerge/--merge-list.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((cnv_intersect_filter_type & CNV_COUNT) && (!(cnv_calc_type & (CNV_SAMPLE_PERM | CNV_ENRICHMENT_TEST)))) {
-    logprint("Error: --cnv-count must be used with --cnv-indiv-perm or --cnv-enrichment-test.\n");
+    logerrprint("Error: --cnv-count must be used with --cnv-indiv-perm or --cnv-enrichment-test.\n");
     goto main_ret_INVALID_CMDLINE;
   }
   if (!phenoname) {
     if ((filter_flags & FILTER_PRUNE) && (!(fam_cols & FAM_COL_6))) {
-      logprint("Error: --prune and --no-pheno cannot coexist without an alternate phenotype\nfile.\n");
+      logerrprint("Error: --prune and --no-pheno cannot coexist without an alternate phenotype\nfile.\n");
       goto main_ret_INVALID_CMDLINE_A;
     } else if (pheno_modifier & PHENO_ALL) {
-      logprint("Error: --all-pheno must be used with --pheno.\n");
+      logerrprint("Error: --all-pheno must be used with --pheno.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   } else if (read_dists_fname && (!(calculation_type & (CALC_CLUSTER | CALC_IBS_TEST | CALC_GROUPDIST | CALC_NEIGHBOR | CALC_REGRESS_DISTANCE)))) {
-    logprint("Error: --read-dists cannot be used without --cluster, --ibs-test/--groupdist,\n--neighbour, or --regress-distance.\n");
+    logerrprint("Error: --read-dists cannot be used without --cluster, --ibs-test/--groupdist,\n--neighbour, or --regress-distance.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((cluster.ppc != 0.0) && (!read_genome_fname) && (calculation_type & (CALC_DISTANCE))) {
-    logprint("Error: --ppc cannot be used with --distance without --read-genome.\n");
+    logerrprint("Error: --ppc cannot be used with --distance without --read-genome.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((calculation_type & (CALC_CLUSTER | CALC_NEIGHBOR)) && (((calculation_type & CALC_DISTANCE) && (dist_calc_type & (DISTANCE_1_MINUS_IBS | DISTANCE_ALCT))) || (calculation_type & CALC_PLINK1_DISTANCE_MATRIX)) && (!(calculation_type & CALC_GENOME))) {
     // actually allow this for now if --genome present, since it auto-clobbers
     // the wrong-unit distance matrix
-    logprint("Error: --cluster and --neighbour cannot be used with non-IBS distance matrix\ncalculations.\n");
+    logerrprint("Error: --cluster and --neighbour cannot be used with non-IBS distance matrix\ncalculations.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if (matrix_flag_state == 1) {
@@ -12627,33 +12690,33 @@ int32_t main(int32_t argc, char** argv) {
   }
   if (calculation_type & CALC_PLINK1_IBS_MATRIX) {
     if (distance_wts_fname || (distance_exp != 0.0)) {
-      logprint("Error: --ibs-matrix cannot be used with --distance-wts.\n");
+      logerrprint("Error: --ibs-matrix cannot be used with --distance-wts.\n");
       goto main_ret_INVALID_CMDLINE;
     }
     if (dist_calc_type & DISTANCE_IBS) {
-      logprint("Error: --ibs-matrix cannot be used with '--distance ibs'.\n");
+      logerrprint("Error: --ibs-matrix cannot be used with '--distance ibs'.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
     if (read_genome_fname && (cluster.ppc == 0.0)) {
-      logprint("Error: --read-genome is pointless with --ibs-matrix unless --ppc is also\npresent.\n");
+      logerrprint("Error: --read-genome is pointless with --ibs-matrix unless --ppc is also\npresent.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
     if (read_dists_fname) {
-      logprint("Error: --read-dists cannot be used with a distance matrix calculation.\n");
+      logerrprint("Error: --read-dists cannot be used with a distance matrix calculation.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
     if (parallel_tot > 1) {
-      logprint("Error: --parallel and --ibs-matrix cannot be used together.  Use --distance\ninstead.\n");
+      logerrprint("Error: --parallel and --ibs-matrix cannot be used together.  Use --distance\ninstead.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
   if (distance_wts_fname && (!(calculation_type & (CALC_DISTANCE | CALC_RELATIONSHIP)))) {
-    logprint("Error: --distance-wts must be used with --distance, --make-rel, --make-grm-bin,\nor --make-grm-gz.\n");
+    logerrprint("Error: --distance-wts must be used with --distance, --make-rel, --make-grm-bin,\nor --make-grm-gz.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((parallel_tot > 1) && (!(calculation_type & (CALC_LD | CALC_DISTANCE | CALC_GENOME | CALC_RELATIONSHIP)))) {
     if ((!(calculation_type & CALC_EPI)) || (!(epi_info.modifier & (EPI_FAST | EPI_REG)))) {
-      logprint("Error: --parallel only affects --r/--r2, --distance, --genome, --make-rel,\n--make-grm-gz/--make-grm-bin, and --epistasis/--fast-epistasis.\n");
+      logerrprint("Error: --parallel only affects --r/--r2, --distance, --genome, --make-rel,\n--make-grm-gz/--make-grm-bin, and --epistasis/--fast-epistasis.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
@@ -12667,107 +12730,107 @@ int32_t main(int32_t argc, char** argv) {
   }
   if (load_rare == LOAD_RARE_CNV) {
     if (filter_flags & FILTER_NOCNV) {
-      logprint("Error: .cnv fileset specified with incompatible filtering flag(s).  (Check if\nthere is a --cnv-... flag with the functionality you're looking for.)\n");
+      logerrprint("Error: .cnv fileset specified with incompatible filtering flag(s).  (Check if\nthere is a --cnv-... flag with the functionality you're looking for.)\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   } else if (load_rare == LOAD_RARE_DOSAGE) {
     if (filter_flags & FILTER_NODOSAGE) {
-      logprint("Error: --dosage used with incompatible filtering flag(s).\n");
+      logerrprint("Error: --dosage used with incompatible filtering flag(s).\n");
       goto main_ret_INVALID_CMDLINE_A;
     } else if ((!mapname[0]) && (filter_flags & FILTER_DOSAGEMAP)) {
-      logprint("Error: --dosage cannot be used with variant filters unless a .map file is\nspecified.\n");
+      logerrprint("Error: --dosage cannot be used with variant filters unless a .map file is\nspecified.\n");
       goto main_ret_INVALID_CMDLINE_A;
     } else if (!famname[0]) {
-      logprint("Error: --dosage must be used with --fam.\n");
+      logerrprint("Error: --dosage must be used with --fam.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
   if ((family_info.mendel_modifier & MENDEL_DUOS) && (!(calculation_type & CALC_MENDEL)) && (!(family_info.mendel_modifier & MENDEL_FILTER)) && (!(misc_flags & MISC_SET_ME_MISSING))) {
-    logprint("Error: --mendel-duos must be used with --me/--mendel/--set-me-missing.\n");
+    logerrprint("Error: --mendel-duos must be used with --me/--mendel/--set-me-missing.\n");
     goto main_ret_INVALID_CMDLINE;
   } else if ((family_info.mendel_modifier & MENDEL_MULTIGEN) && (!(calculation_type & (CALC_MENDEL | CALC_TDT | CALC_DFAM | CALC_QFAM))) && (!(family_info.mendel_modifier & MENDEL_FILTER)) && (!(misc_flags & MISC_SET_ME_MISSING))) {
-    logprint("Error: --mendel-multigen must be used with --me, --mendel, --set-me-missing, or\nan association test which checks for Mendel errors.\n");
+    logerrprint("Error: --mendel-multigen must be used with --me, --mendel, --set-me-missing, or\nan association test which checks for Mendel errors.\n");
     goto main_ret_INVALID_CMDLINE;
   }
   if (flip_subset_fname && (load_rare || (calculation_type != CALC_MAKE_BED) || (min_maf != 0.0) || (max_maf != 0.5) || (hwe_thresh != 0.0))) {
-    logprint("Error: --flip-subset must be used with --flip, --make-bed, and no other\ncommands or MAF-based filters.\n");
+    logerrprint("Error: --flip-subset must be used with --flip, --make-bed, and no other\ncommands or MAF-based filters.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if (calculation_type & CALC_RECODE) {
     if (recode_modifier & RECODE_23) {
       if (chrom_info.species != SPECIES_HUMAN) {
-	logprint("Error: --recode 23 can only be used on human data.\n");
+	logerrprint("Error: --recode 23 can only be used on human data.\n");
 	goto main_ret_INVALID_CMDLINE;
       }
       if ((recode_modifier & RECODE_23) && (misc_flags & MISC_ALLOW_EXTRA_CHROMS) && (!chrom_info.zero_extra_chroms)) {
-	logprint("Error: --allow-extra-chr requires the '0' modifier when used with --recode 23.\n");
+	logerrprint("Error: --allow-extra-chr requires the '0' modifier when used with --recode 23.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if ((recode_modifier & RECODE_BEAGLE) && chrom_info.zero_extra_chroms) {
-        logprint("Error: --allow-extra-chr cannot have the '0' modifier when used with\n--recode beagle.\n");
+        logerrprint("Error: --allow-extra-chr cannot have the '0' modifier when used with\n--recode beagle.\n");
 	goto main_ret_INVALID_CMDLINE;
       }
     } else if ((recode_modifier & RECODE_BIMBAM) && (misc_flags & MISC_ALLOW_EXTRA_CHROMS) && (!chrom_info.zero_extra_chroms)) {
-      logprint("Error: --allow-extra-chr requires the '0' modifier when used with\n--recode bimbam.\n");
+      logerrprint("Error: --allow-extra-chr requires the '0' modifier when used with\n--recode bimbam.\n");
       goto main_ret_INVALID_CMDLINE;
     }
   }
   if (sex_missing_pheno & MUST_HAVE_SEX) {
     if (load_rare & LOAD_RARE_CNV) {
       if (!(cnv_calc_type & CNV_WRITE)) {
-        logprint("Error: --must-have-sex must be used with --cnv-write.\n");
+        logerrprint("Error: --must-have-sex must be used with --cnv-write.\n");
         goto main_ret_INVALID_CMDLINE;
       }
     } else {
       if (!(calculation_type & (CALC_WRITE_COVAR | CALC_MAKE_BED | CALC_MAKE_FAM | CALC_RECODE))) {
-        logprint("Error: --must-have-sex must be used with --make-bed, --make-just-fam, --recode,\nor --write-covar.\n");
+        logerrprint("Error: --must-have-sex must be used with --make-bed, --make-just-fam, --recode,\nor --write-covar.\n");
         goto main_ret_INVALID_CMDLINE;
       }
     }
   }
   if (misc_flags & MISC_IMPUTE_SEX) {
     if (!(calculation_type & (CALC_WRITE_COVAR | CALC_MAKE_BED | CALC_RECODE))) {
-      logprint("Error: --impute-sex must be used with --make-bed/--recode/--write-covar.\n");
+      logerrprint("Error: --impute-sex must be used with --make-bed/--recode/--write-covar.\n");
       goto main_ret_INVALID_CMDLINE_A;
     } else if (calculation_type & (~(CALC_WRITE_COVAR | CALC_MAKE_BED | CALC_RECODE | CALC_SEXCHECK))) {
-      logprint("Error: --impute-sex cannot be used with any commands other than\n--make-bed/--recode/--write-covar.\n");
+      logerrprint("Error: --impute-sex cannot be used with any commands other than\n--make-bed/--recode/--write-covar.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
   if (cluster.qmatch_fname && (!cluster.qt_fname)) {
-    logprint("Error: --qt must be used with --qmatch.\n");
+    logerrprint("Error: --qt must be used with --qmatch.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if (mwithin_col && (!loop_assoc_fname) && (!cluster.fname)) {
-    logprint("Error: --mwithin must be used with --within/--loop-assoc.\n");
+    logerrprint("Error: --mwithin must be used with --within/--loop-assoc.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((!cluster.fname) && (!(misc_flags & MISC_FAMILY_CLUSTERS))) {
     if (cluster.keep_fname) {
-      logprint("Error: --keep-clusters must be used with --within/--family.\n");
+      logerrprint("Error: --keep-clusters must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (cluster.keep_flattened) {
-      logprint("Error: --keep-cluster-names must be used with --within/--family.\n");
+      logerrprint("Error: --keep-cluster-names must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (cluster.remove_fname) {
-      logprint("Error: --remove-clusters must be used with --within/--family.\n");
+      logerrprint("Error: --remove-clusters must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (cluster.remove_flattened) {
-      logprint("Error: --remove-cluster-names must be used with --within/--family.\n");
+      logerrprint("Error: --remove-cluster-names must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (rel_info.pca_cluster_names_flattened) {
-      logprint("Error: --pca-cluster-names must be used with --within/--family.\n");
+      logerrprint("Error: --pca-cluster-names must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (rel_info.pca_clusters_fname) {
-      logprint("Error: --pca-clusters must be used with --within/--family.\n");
+      logerrprint("Error: --pca-clusters must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (calculation_type & CALC_CMH) {
-      logprint("Error: --mh/--bd/--mh2 must be used with --within/--family.\n");
+      logerrprint("Error: --mh/--bd/--mh2 must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (calculation_type & CALC_HOMOG) {
-      logprint("Error: --homog must be used with --within/--family.\n");
+      logerrprint("Error: --homog must be used with --within/--family.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if ((calculation_type & CALC_FST) && (!(misc_flags & MISC_FST_CC))) {
-      logprint("Error: --fst should be used with --within, unless the 'case-control' modifier\nis specified.\n");
+      logerrprint("Error: --fst should be used with --within, unless the 'case-control' modifier\nis specified.\n");
       goto main_ret_INVALID_CMDLINE;
     }
   }
@@ -12775,84 +12838,84 @@ int32_t main(int32_t argc, char** argv) {
     if (set_info.modifier) {
       if (set_info.modifier & SET_COMPLEMENTS) {
 	if (set_info.merged_set_name) {
-	  logprint("Error: --make-set-complement-all must be used with --set/--make-set.\n");
+	  logerrprint("Error: --make-set-complement-all must be used with --set/--make-set.\n");
 	} else {
-	  logprint("Error: --complement-sets must be used with --set/--make-set.\n");
+	  logerrprint("Error: --complement-sets must be used with --set/--make-set.\n");
 	}
       } else { // only remaining possibility for now
-        logprint("Error: --gene-all must be used with --set/--make-set.\n");
+        logerrprint("Error: --gene-all must be used with --set/--make-set.\n");
       }
       goto main_ret_INVALID_CMDLINE;
     } else if (set_info.genekeep_flattened) {
-      logprint("Error: --gene must be used with --set/--make-set.\n");
+      logerrprint("Error: --gene must be used with --set/--make-set.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (model_modifier & MODEL_SET_TEST) {
-      logprint("Error: --assoc/--model set-test must be used with --set/--make-set.\n");
+      logerrprint("Error: --assoc/--model set-test must be used with --set/--make-set.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (glm_modifier & GLM_SET_TEST) {
-      logprint("Error: --linear/--logistic set-test must be used with --set/--make-set.\n");
+      logerrprint("Error: --linear/--logistic set-test must be used with --set/--make-set.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (family_info.tdt_modifier & TDT_SET_TEST) {
-      logprint("Error: --tdt set-test must be used with --set/--make-set.\n");
+      logerrprint("Error: --tdt set-test must be used with --set/--make-set.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (cluster.modifier & CLUSTER_CMH_SET_TEST) {
-      logprint("Error: --mh/--bd set-test must be used with --set/--make-set.\n");
+      logerrprint("Error: --mh/--bd set-test must be used with --set/--make-set.\n");
       goto main_ret_INVALID_CMDLINE;
     }
   } else {
     uii = 0;
     if (model_modifier & MODEL_SET_TEST) {
       if ((!(model_modifier & MODEL_PERM)) && (!model_mperm_val)) {
-        logprint("Error: --assoc/--model set-test requires permutation.\n");
+        logerrprint("Error: --assoc/--model set-test requires permutation.\n");
         goto main_ret_INVALID_CMDLINE_A;
       } else if (model_modifier & MODEL_FISHER) {
-	logprint("Error: --assoc/--model set-test cannot be used with Fisher's exact test.\n");
+	logerrprint("Error: --assoc/--model set-test cannot be used with Fisher's exact test.\n");
         goto main_ret_INVALID_CMDLINE_A;
       } else if (model_modifier & MODEL_PGEN) {
-	logprint("Error: --model set-test cannot be used with 2df genotypic chi-square stats.\n");
+	logerrprint("Error: --model set-test cannot be used with 2df genotypic chi-square stats.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       } else if (model_modifier & MODEL_LIN) {
-	logprint("Error: --assoc set-test does not currently support the Lin statistic; contact\nthe developers if you need this combination.\n");
+	logerrprint("Error: --assoc set-test does not currently support the Lin statistic; contact\nthe developers if you need this combination.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       }
       uii = 1;
     }
     if (glm_modifier & GLM_SET_TEST) {
       if ((!(glm_modifier & GLM_PERM)) && (!glm_mperm_val)) {
-        logprint("Error: --linear/--logistic set-test requires permutation.\n");
+        logerrprint("Error: --linear/--logistic set-test requires permutation.\n");
         goto main_ret_INVALID_CMDLINE_A;
       } else if ((glm_modifier & (GLM_GENOTYPIC | GLM_HETHOM | GLM_TEST_ALL)) || tests_range_list.name_ct) {
-	logprint("Error: --linear/--logistic set-test cannot be used with joint tests.\n");
+	logerrprint("Error: --linear/--logistic set-test cannot be used with joint tests.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       }
       uii = 1;
     }
     if (family_info.tdt_modifier & TDT_SET_TEST) {
       if (!(family_info.tdt_modifier & (TDT_PERM | TDT_MPERM))) {
-        logprint("Error: --tdt set-test requires permutation.\n");
+        logerrprint("Error: --tdt set-test requires permutation.\n");
         goto main_ret_INVALID_CMDLINE_A;
       }
-      logprint("Error: --tdt set-test is currently under development.\n");
+      logerrprint("Error: --tdt set-test is currently under development.\n");
       retval = RET_CALC_NOT_YET_SUPPORTED;
       goto main_ret_1;
       uii = 1;
     }
     if (family_info.dfam_modifier & DFAM_SET_TEST) {
       if (!(family_info.dfam_modifier & (DFAM_PERM | DFAM_MPERM))) {
-        logprint("Error: --dfam set-test requires permutation.\n");
+        logerrprint("Error: --dfam set-test requires permutation.\n");
         goto main_ret_INVALID_CMDLINE_A;
       }
-      logprint("Error: --dfam set-test is currently under development.\n");
+      logerrprint("Error: --dfam set-test is currently under development.\n");
       retval = RET_CALC_NOT_YET_SUPPORTED;
       goto main_ret_1;
       uii = 1;
     }
     if (cluster.modifier & CLUSTER_CMH_SET_TEST) {
       if (!(cluster.modifier & (CLUSTER_CMH_PERM | CLUSTER_CMH_MPERM))) {
-        logprint("Error: --mh/--bd set-test requires permutation.\n");
+        logerrprint("Error: --mh/--bd set-test requires permutation.\n");
         goto main_ret_INVALID_CMDLINE_A;
       }
-      logprint("Error: --mh/--bd set-test is currently under development.\n");
+      logerrprint("Error: --mh/--bd set-test is currently under development.\n");
       retval = RET_CALC_NOT_YET_SUPPORTED;
       goto main_ret_1;
       uii = 1;
@@ -12873,7 +12936,7 @@ int32_t main(int32_t argc, char** argv) {
     }
   }
   if ((family_info.tdt_modifier & (TDT_POO | TDT_PARENPERM1 | TDT_PARENPERM2 | TDT_POOPERM_PAT | TDT_POOPERM_MAT)) && (!(calculation_type & CALC_TDT))) {
-    logprint("Error: --poo/--parentdt1/--parentdt2/--pat/--mat must be used with --tdt.\n");
+    logerrprint("Error: --poo/--parentdt1/--parentdt2/--pat/--mat must be used with --tdt.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if (calculation_type & CALC_FLIPSCAN) {
@@ -12896,7 +12959,7 @@ int32_t main(int32_t argc, char** argv) {
   }
   if (calculation_type & CALC_BLOCKS) {
     if (ld_info.blocks_recomb_highci > ld_info.blocks_strong_highci) {
-      logprint("Error: --blocks-recomb-highci value cannot be larger than\n--blocks-strong-highci value.\n");
+      logerrprint("Error: --blocks-recomb-highci value cannot be larger than\n--blocks-strong-highci value.\n");
       goto main_ret_INVALID_CMDLINE;
     }
     if (ld_info.blocks_max_bp == 0xffffffffU) {
@@ -12911,33 +12974,33 @@ int32_t main(int32_t argc, char** argv) {
   if ((!(calculation_type & CALC_LD)) || ((calculation_type & CALC_LD) && (ld_info.modifier & (LD_MATRIX_SHAPEMASK | LD_INTER_CHR)))) {
     if ((ld_info.snpstr || ld_info.snps_rl.name_ct) && (!(ld_info.modifier & LD_INTER_CHR))) {
       if (calculation_type & CALC_LD) {
-	logprint("Error: --ld-snp/--ld-snps/--ld-snp-list cannot be used with the --r/--r2 matrix\noutput modifiers.\n");
+	logerrprint("Error: --ld-snp/--ld-snps/--ld-snp-list cannot be used with the --r/--r2 matrix\noutput modifiers.\n");
       } else {
-        logprint("Error: --ld-snp/--ld-snps/--ld-snp-list must be used with --r/--r2.\n");
+        logerrprint("Error: --ld-snp/--ld-snps/--ld-snp-list must be used with --r/--r2.\n");
       }
       goto main_ret_INVALID_CMDLINE_A;
     } else if (ld_info.window_size != 0xffffffffU) {
       if (calculation_type & CALC_LD) {
-	logprint("Error: --ld-window flag cannot be used with the --r/--r2 'inter-chr' or matrix\noutput modifiers.\n");
+	logerrprint("Error: --ld-window flag cannot be used with the --r/--r2 'inter-chr' or matrix\noutput modifiers.\n");
         goto main_ret_INVALID_CMDLINE_A;
       } else if (!(calculation_type & (CALC_BLOCKS | CALC_FLIPSCAN))) {
-        logprint("Error: --ld-window flag must be used with --r/--r2.\n");
+        logerrprint("Error: --ld-window flag must be used with --r/--r2.\n");
         goto main_ret_INVALID_CMDLINE_A;
       }
     } else if (ld_info.window_bp != 0xffffffffU) {
       if (calculation_type & CALC_LD) {
-	logprint("Error: --ld-window-kb flag cannot be used with the --r/--r2 'inter-chr' or\nmatrix output modifiers.\n");
+	logerrprint("Error: --ld-window-kb flag cannot be used with the --r/--r2 'inter-chr' or\nmatrix output modifiers.\n");
         goto main_ret_INVALID_CMDLINE_A;
       } else if (!(calculation_type & CALC_BLOCKS)) {
-        logprint("Error: --ld-window-kb flag must be used with --r/--r2.\n");
+        logerrprint("Error: --ld-window-kb flag must be used with --r/--r2.\n");
         goto main_ret_INVALID_CMDLINE_A;
       }
     } else if ((ld_info.window_r2 != 0.2) && (!(ld_info.modifier & LD_INTER_CHR))) {
       if (!(ld_info.modifier & LD_R2)) {
-        logprint("Error: --ld-window-r2 flag must be used with --r2.\n");
+        logerrprint("Error: --ld-window-r2 flag must be used with --r2.\n");
         goto main_ret_INVALID_CMDLINE;
       } else {
-	logprint("Error: --ld-window-r2 flag cannot be used with the --r2 matrix output modifiers.\n");
+	logerrprint("Error: --ld-window-r2 flag cannot be used with the --r2 matrix output modifiers.\n");
 	goto main_ret_INVALID_CMDLINE_A;
       }
     }
@@ -12950,7 +13013,7 @@ int32_t main(int32_t argc, char** argv) {
     }
   }
   if ((ld_info.modifier & LD_DPRIME) && (!(calculation_type & CALC_LD))) {
-    logprint("Error: --D/--dprime must be used with --r/--r2.\n");
+    logerrprint("Error: --D/--dprime must be used with --r/--r2.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
 
@@ -12972,7 +13035,7 @@ int32_t main(int32_t argc, char** argv) {
       uii = next_set(chrom_info.chrom_mask, uii + 1, CHROM_MASK_INITIAL_WORDS * BITCT);
     }
     if (((uii == CHROM_MASK_INITIAL_WORDS * BITCT) && chrom_info.incl_excl_name_stack) || ((uii != CHROM_MASK_INITIAL_WORDS * BITCT) && (uii || (!chrom_info.incl_excl_name_stack) || chrom_info.incl_excl_name_stack->next))) {
-      logprint("Error: --from-bp/-kb/-mb and --to-bp/-kb/-mb require a single chromosome to be\nidentified (either explicitly with --chr, or implicitly with --from/--to).\n");
+      logerrprint("Error: --from-bp/-kb/-mb and --to-bp/-kb/-mb require a single chromosome to be\nidentified (either explicitly with --chr, or implicitly with --from/--to).\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
@@ -12996,7 +13059,7 @@ int32_t main(int32_t argc, char** argv) {
     }
     if (uii != 1) {
       // prevent one permutation test's values from clobbering another's
-      logprint("Error: --mperm-save{-all} must be used with exactly one max(T) permutation\ntest.\n");
+      logerrprint("Error: --mperm-save{-all} must be used with exactly one max(T) permutation\ntest.\n");
       goto main_ret_INVALID_CMDLINE;
     }
   }
@@ -13004,7 +13067,7 @@ int32_t main(int32_t argc, char** argv) {
     if (!(model_modifier & (MODEL_ASSOC | MODEL_PDOM | MODEL_PREC | MODEL_PTREND))) {
       if (mtest_adjust && (!(model_modifier & MODEL_SET_TEST))) {
 	// this is actually okay with the set test
-	logprint("Error: In order to use --model with --adjust, you must include the 'trend',\n'trend-only', 'dom', or 'rec' modifier.\n");
+	logerrprint("Error: In order to use --model with --adjust, you must include the 'trend',\n'trend-only', 'dom', or 'rec' modifier.\n");
 	goto main_ret_INVALID_CMDLINE;
       }
     }
@@ -13016,51 +13079,51 @@ int32_t main(int32_t argc, char** argv) {
     }
   }
   if ((mtest_adjust & (ADJUST_LAMBDA + 1)) == ADJUST_LAMBDA) {
-    logprint("Error: --lambda must be used with --adjust.\n");
+    logerrprint("Error: --lambda must be used with --adjust.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((homozyg.modifier & (HOMOZYG_GROUP | HOMOZYG_GROUP_VERBOSE)) && (!(calculation_type & CALC_HOMOZYG))) {
     if (homozyg.overlap_min == 0.95) {
-      logprint("Error: --homozyg-group must be used with another --homozyg... flag.\n");
+      logerrprint("Error: --homozyg-group must be used with another --homozyg... flag.\n");
     } else {
-      logprint("Error: --homozyg-match must be used with another --homozyg... flag.\n");
+      logerrprint("Error: --homozyg-match must be used with another --homozyg... flag.\n");
     }
     goto main_ret_INVALID_CMDLINE_A;
   }
   if (score_info.data_fname) {
     if (!score_info.range_fname) {
-      logprint("Error: --q-score-file cannot be used without --q-score-range.\n");
+      logerrprint("Error: --q-score-file cannot be used without --q-score-range.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (!(calculation_type & CALC_SCORE)) {
-      logprint("Error: --q-score-range must be used with --score.\n");
+      logerrprint("Error: --q-score-range must be used with --score.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
   if (qual_max_thresh != HUGE_DOUBLE) {
     if (!qual_filter) {
-      logprint("Error: --qual-max-threshold must be used with --qual-scores.\n");
+      logerrprint("Error: --qual-max-threshold must be used with --qual-scores.\n");
       goto main_ret_INVALID_CMDLINE;
     } else if (qual_max_thresh < qual_min_thresh) {
-      logprint("Error: --qual-max-threshold value cannot be negative unless --qual-threshold is\nalso present.\n");
+      logerrprint("Error: --qual-max-threshold value cannot be negative unless --qual-threshold is\nalso present.\n");
       goto main_ret_INVALID_CMDLINE_A;
     }
   }
   if (gene_report_border && (!gene_report_fname)) {
-    logprint("Error: --gene-list-border must be used with --gene-report.\n");
+    logerrprint("Error: --gene-list-border must be used with --gene-report.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
 
   uii = load_params & LOAD_PARAMS_OX_ALL;
   if ((uii == LOAD_PARAMS_OXGEN) || (uii == LOAD_PARAMS_OXBGEN)) {
-    logprint("Error: --gen/--bgen cannot be used without --data or --sample.\n");
+    logerrprint("Error: --gen/--bgen cannot be used without --data or --sample.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((load_rare & LOAD_RARE_TFAM) && (!(load_rare & (LOAD_RARE_TRANSPOSE | LOAD_RARE_TPED)))) {
-    logprint("Error: --tfam must be used with --tfile or --tped.\n");
+    logerrprint("Error: --tfam must be used with --tfile or --tped.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   if ((merge_type & MERGE_EQUAL_POS) && (!(calculation_type & CALC_MERGE))) {
-    logprint("Error: --merge-equal-pos must be used with --merge/--bmerge/--merge-list.\n(Note that you are permitted to merge a fileset with itself.)\n");
+    logerrprint("Error: --merge-equal-pos must be used with --merge/--bmerge/--merge-list.\n(Note that you are permitted to merge a fileset with itself.)\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
   // short batch job?
@@ -13074,7 +13137,7 @@ int32_t main(int32_t argc, char** argv) {
     // otherwise, autoconversion job
   }
   if (!(load_params || load_rare || uii || (merge_type & MERGE_LIST))) {
-    logprint("Error: No input dataset.\n");
+    logerrprint("Error: No input dataset.\n");
     goto main_ret_INVALID_CMDLINE_A;
   }
 
@@ -13193,11 +13256,11 @@ int32_t main(int32_t argc, char** argv) {
   if (load_rare == LOAD_RARE_DOSAGE) {
     if (calculation_type & (~CALC_SCORE)) {
       // with --dosage, there *can't* be anything else to do
-      logprint("Error: --dosage cannot be used with other PLINK computations.\n");
+      logerrprint("Error: --dosage cannot be used with other PLINK computations.\n");
       goto main_ret_INVALID_CMDLINE;
     }
     pigz_init(g_thread_ct);
-    retval = plink1_dosage(&dosage_info, famname, mapname, outname, outname_end, phenoname, extractname, excludename, keepname, removename, keepfamname, removefamname, filtername, makepheno_str, phenoname_str, covar_fname, qual_filter, update_map, update_name, update_ids_fname, update_parents_fname, update_sex_fname, filtervals_flattened, filter_attrib_fname, filter_attrib_liststr, filter_attrib_sample_fname, filter_attrib_sample_liststr, qual_min_thresh, qual_max_thresh, thin_keep_prob, [...]
+    retval = plink1_dosage(&dosage_info, famname, mapname, outname, outname_end, phenoname, extractname, excludename, keepname, removename, keepfamname, removefamname, filtername, makepheno_str, phenoname_str, covar_fname, qual_filter, update_map, update_name, update_ids_fname, update_parents_fname, update_sex_fname, filtervals_flattened, filter_attrib_fname, filter_attrib_liststr, filter_attrib_sample_fname, filter_attrib_sample_liststr, qual_min_thresh, qual_max_thresh, thin_keep_prob, [...]
     // unconditional; note that plink1_dosage() currently doesn't even bother
     // to pop stuff off the stack when it's done
     goto main_ret_1;
@@ -13221,7 +13284,7 @@ int32_t main(int32_t argc, char** argv) {
 #endif
   } else if (load_rare & LOAD_RARE_CNV) {
 #ifdef HIGH_MAX_CHROM
-    logprint("Error: The CNV module is disabled in high-contig-limit PLINK builds.\n");
+    logerrprint("Error: The CNV module is disabled in high-contig-limit PLINK builds.\n");
     goto main_ret_INVALID_CMDLINE;
     // no, I don't care about unused variable compiler warnings in this case
 #else
@@ -13232,10 +13295,10 @@ int32_t main(int32_t argc, char** argv) {
   } else {
     if (filter_flags) {
       if (!calculation_type) {
-	logprint("Error: Basic file conversions do not support regular filtering operations.\nRerun your command with --make-bed.\n");
+	logerrprint("Error: Basic file conversions do not support regular filtering operations.\nRerun your command with --make-bed.\n");
 	goto main_ret_INVALID_CMDLINE;
       } else if (calculation_type == CALC_MERGE) {
-	logprint("Error: Basic merge does not support regular filtering operations.  Rerun your\ncommand with --make-bed.\n");
+	logerrprint("Error: Basic merge does not support regular filtering operations.  Rerun your\ncommand with --make-bed.\n");
 	goto main_ret_INVALID_CMDLINE;
       }
     }
@@ -13293,7 +13356,7 @@ int32_t main(int32_t argc, char** argv) {
     } else if (!rel_info.ibc_type) {
       rel_info.ibc_type = 1;
     }
-    retval = plink(outname, outname_end, pedname, mapname, famname, cm_map_fname, cm_map_chrname, phenoname, extractname, excludename, keepname, removename, keepfamname, removefamname, filtername, freqname, distance_wts_fname, read_dists_fname, read_dists_id_fname, evecname, mergename1, mergename2, mergename3, missing_mid_template, missing_marker_id_match, makepheno_str, phenoname_str, a1alleles, a2alleles, recode_allele_name, covar_fname, update_alleles_fname, read_genome_fname, qual_fi [...]
+    retval = plink(outname, outname_end, pedname, mapname, famname, cm_map_fname, cm_map_chrname, phenoname, extractname, excludename, keepname, removename, keepfamname, removefamname, filtername, freqname, distance_wts_fname, read_dists_fname, read_dists_id_fname, evecname, mergename1, mergename2, mergename3, missing_mid_template, missing_marker_id_match, makepheno_str, phenoname_str, a1alleles, a2alleles, recode_allele_name, covar_fname, update_alleles_fname, read_genome_fname, qual_fi [...]
   }
   while (0) {
   main_ret_NOMEM:
@@ -13307,31 +13370,35 @@ int32_t main(int32_t argc, char** argv) {
     break;
   main_ret_INVALID_CMDLINE_UNRECOGNIZED:
     invalid_arg(argv[cur_arg]);
-    logprintb();
-    logprint(errstr_append);
+    logerrprintb();
+    logerrprint(errstr_append);
     retval = RET_INVALID_CMDLINE;
     break;
   main_ret_INVALID_CMDLINE_INPUT_CONFLICT:
-    LOGPRINTF("Error: --%s conflicts with another input flag.\n%s", argptr, errstr_append);
+    LOGERRPRINTF("Error: --%s conflicts with another input flag.\n%s", argptr, errstr_append);
     retval = RET_INVALID_CMDLINE;
     break;
   main_ret_INVALID_CMDLINE_WWA:
     wordwrap(logbuf, 0);
   main_ret_INVALID_CMDLINE_2A:
-    logprintb();
+    logerrprintb();
   main_ret_INVALID_CMDLINE_A:
-    logprint(errstr_append);
+    logerrprint(errstr_append);
     retval = RET_INVALID_CMDLINE;
     break;
   main_ret_INVALID_CMDLINE_WW:
     wordwrap(logbuf, 0);
   main_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
   main_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
   main_ret_NULL_CALC:
-    logprint("Warning: No output requested.  Exiting.\n");
+    if (filter_flags) {
+      logerrprint("Warning: No output requested.  (Did you forget --make-bed?)  Exiting.\n");
+    } else {
+      logerrprint("Warning: No output requested.  Exiting.\n");
+    }
     fputs(cmdline_format_str, stdout);
     fputs(notestr_null_calc2, stdout);
     retval = RET_NULL_CALC;
@@ -13342,7 +13409,7 @@ int32_t main(int32_t argc, char** argv) {
     memcpy(logbuf, "Error: --", 9);
     strcpy(sptr, " is either unfinished or not yet well-tested. If you wish to help with testing, use the latest development build.\n");
     wordwrap(logbuf, 0);
-    logprintb();
+    logerrprintb();
     retval = RET_CALC_NOT_YET_SUPPORTED;
 #endif
   }
@@ -13464,7 +13531,8 @@ int32_t main(int32_t argc, char** argv) {
       time(&rawtime);
       logstr(ctime(&rawtime));
       if (fclose(logfile)) {
-	fputs("Error: Failed to finish writing to log.\n", stdout);
+	fflush(stdout);
+	fputs("Error: Failed to finish writing to log.\n", stderr);
       }
     } else {
       fclose(logfile);
diff --git a/plink_assoc.c b/plink_assoc.c
index d766b87..c597a3f 100644
--- a/plink_assoc.c
+++ b/plink_assoc.c
@@ -6971,7 +6971,7 @@ int32_t model_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, cha
   char* a2ptr;
   uint32_t loop_end;
   if (pheno_nm_ct < 2) {
-    logprint("Warning: Skipping --assoc/--model since less than two phenotypes are present.\n");
+    logerrprint("Warning: Skipping --assoc/--model since less than two phenotypes are present.\n");
     goto model_assoc_ret_1;
   }
   if (max_marker_allele_len > MAXLINELEN) {
@@ -7074,7 +7074,7 @@ int32_t model_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, cha
     }
   } else {
     if (is_set(chrom_info_ptr->haploid_mask, 0)) {
-      logprint("Error: --model cannot be used on haploid genomes.\n");
+      logerrprint("Error: --model cannot be used on haploid genomes.\n");
       goto model_assoc_ret_INVALID_CMDLINE;
     }
     uii = count_non_autosomal_markers(chrom_info_ptr, marker_exclude, 0, 1);
@@ -7084,13 +7084,13 @@ int32_t model_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, cha
 	// the command-line fix is, this is not worth the trouble of supporting
 	// (this problem illustrates why core data structures should use
 	// unfiltered indexes when possible, though)
-	logprint("Error: --model set-test cannot be used with sets containing MT/haploid\nvariants.  (You can use e.g. '--not-chr y, mt' to exclude them.)\n");
+	logerrprint("Error: --model set-test cannot be used with sets containing MT/haploid\nvariants.  (You can use e.g. '--not-chr y, mt' to exclude them.)\n");
 	goto model_assoc_ret_INVALID_CMDLINE;
       }
       LOGPRINTF("Excluding %u MT/haploid variant%s from --model analysis.\n", uii, (uii == 1)? "" : "s");
       marker_ct -= uii;
       if (!marker_ct) {
-	logprint("Error: No variants remaining for --model analysis.\n");
+	logerrprint("Error: No variants remaining for --model analysis.\n");
 	goto model_assoc_ret_INVALID_CMDLINE;
       }
     }
@@ -7180,7 +7180,7 @@ int32_t model_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, cha
 	goto model_assoc_ret_1;
       }
       if (!g_cluster_ct) {
-        logprint("Error: No size 2+ clusters for permutation test.\n");
+        logerrprint("Error: No size 2+ clusters for permutation test.\n");
 	goto model_assoc_ret_INVALID_CMDLINE;
       }
       retval = cluster_alloc_and_populate_magic_nums(g_cluster_ct, g_cluster_map, g_cluster_starts, &g_tot_quotients, &g_totq_magics, &g_totq_preshifts, &g_totq_postshifts, &g_totq_incrs);
@@ -9009,7 +9009,7 @@ int32_t qassoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* ou
   char* a1ptr;
   char* a2ptr;
   if (pheno_nm_ct < 2) {
-    logprint("Warning: Skipping QT --assoc since less than two phenotypes are present.\n");
+    logerrprint("Warning: Skipping QT --assoc since less than two phenotypes are present.\n");
     goto qassoc_ret_1;
   }
   if (is_set_test) {
@@ -9105,7 +9105,7 @@ int32_t qassoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* ou
     *outname_end2 = '\0';
   }
   if (haploid_chrom_present(chrom_info_ptr) || mt_exists) {
-    logprint("Warning: QT --assoc doesn't handle X/Y/MT/haploid variants normally (try\n--linear).\n");
+    logerrprint("Warning: QT --assoc doesn't handle X/Y/MT/haploid variants normally (try\n--linear).\n");
   }
   LOGPRINTFWW5("Writing QT --assoc report to %s ... ", outname);
   fflush(stdout);
@@ -9137,7 +9137,7 @@ int32_t qassoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* ou
 	goto qassoc_ret_1;
       }
       if (!g_cluster_ct) {
-        logprint("Error: No size 2+ clusters for permutation test.\n");
+        logerrprint("Error: No size 2+ clusters for permutation test.\n");
         goto qassoc_ret_INVALID_CMDLINE;
       }
       if (wkspace_alloc_ui_checked(&g_sample_to_cluster, pheno_nm_ct * sizeof(int32_t)) ||
@@ -9204,12 +9204,10 @@ int32_t qassoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* ou
   // ----- begin main loop -----
  qassoc_more_perms:
   if (do_perms_nst) {
-    if (perm_adapt_nst) {
-      if (perm_pass_idx) {
-	while (g_first_adapt_check <= g_perms_done) {
-	  // APERM_MAX prevents infinite loop here
-	  g_first_adapt_check += (int32_t)(apip->init_interval + ((int32_t)g_first_adapt_check) * apip->interval_slope);
-	}
+    if (perm_adapt_nst && perm_pass_idx) {
+      while (g_first_adapt_check <= g_perms_done) {
+	// APERM_MAX prevents infinite loop here
+	g_first_adapt_check += (int32_t)(apip->init_interval + ((int32_t)g_first_adapt_check) * apip->interval_slope);
       }
     }
     // g_perm_vec_ct memory allocation dependencies:
@@ -10066,10 +10064,10 @@ int32_t gxe_assoc(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outn
   uint32_t geno_ssq2;
 
   if (group1_size < 3) {
-    logprint("Error: First --gxe group has fewer than three members.\n");
+    logerrprint("Error: First --gxe group has fewer than three members.\n");
     goto gxe_assoc_ret_INVALID_CMDLINE;
   } else if (group2_size < 3) {
-    logprint("Error: Second --gxe group has fewer than three members.\n");
+    logerrprint("Error: Second --gxe group has fewer than three members.\n");
     goto gxe_assoc_ret_INVALID_CMDLINE;
   }
   if (wkspace_alloc_ul_checked(&loadbuf_raw, unfiltered_sample_ctl * 2 * sizeof(intptr_t)) ||
@@ -10158,7 +10156,7 @@ int32_t gxe_assoc(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outn
       group1_size_male = popcount_longs_exclude(sample_male_include2, group2_include2, covar_nm_ctl * 2);
       group2_size_male = male_ct - group1_size_male;
       if ((group1_size_male < 3) || (group2_size_male < 3)) {
-        logprint("Warning: Skipping Y chromosome for --gxe since a group has less than 3 males.\n");
+        logerrprint("Warning: Skipping Y chromosome for --gxe since a group has less than 3 males.\n");
 	skip_y = 1;
       }
       // currently still need to initialize covar_nm_male_raw even on skip_y
@@ -10199,7 +10197,7 @@ int32_t gxe_assoc(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outn
     goto gxe_assoc_ret_OPEN_FAIL;
   }
   if (haploid_chrom_present(chrom_info_ptr) || mt_exists) {
-    logprint("Warning: --gxe doesn't currently handle X/Y/MT/haploid variants properly.\n");
+    logerrprint("Warning: --gxe doesn't currently handle X/Y/MT/haploid variants properly.\n");
   }
   LOGPRINTFWW5("Writing --gxe report to %s ... ", outname);
   fputs("0%", stdout);
@@ -10981,7 +10979,7 @@ int32_t testmiss(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char*
   uint32_t ukk;
   uint32_t umm;
   if ((!case_ct) || (!ctrl_ct)) {
-    logprint("Warning: Skipping --test-missing since at least one case and one control is\nrequired.\n");
+    logerrprint("Warning: Skipping --test-missing since at least one case and one control is\nrequired.\n");
     goto testmiss_ret_1;
   }
   cur_case_ct_recip = 1.0 / ((double)((int32_t)case_ct));
@@ -10991,7 +10989,7 @@ int32_t testmiss(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char*
   if ((y_code == -1) || (!is_set(chrom_info_ptr->chrom_mask, y_code))) {
     skip_y = 1;
   } else if ((!case_ct_y) || (!ctrl_ct_y)) {
-    logprint("Warning: --test-missing is skipping Y chromosome since at least one male case\nand one male control are necessary.\n");
+    logerrprint("Warning: --test-missing is skipping Y chromosome since at least one male case\nand one male control are necessary.\n");
     skip_y = 1;
   }
   if (perm_maxt) {
@@ -11204,7 +11202,7 @@ int32_t testmiss(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char*
 	goto testmiss_ret_1;
       }
       if (!g_cluster_ct) {
-	logprint("Error: No size 2+ clusters for permutation test.\n");
+	logerrprint("Error: No size 2+ clusters for permutation test.\n");
 	goto testmiss_ret_INVALID_CMDLINE;
       }
       retval = cluster_alloc_and_populate_magic_nums(g_cluster_ct, g_cluster_map, g_cluster_starts, &g_tot_quotients, &g_totq_magics, &g_totq_preshifts, &g_totq_postshifts, &g_totq_incrs);
@@ -11728,7 +11726,7 @@ int32_t make_perm_pheno(pthread_t* threads, char* outname, char* outname_end, ui
   uint32_t sample_nmidx;
   uint32_t rshift;
   if (!pheno_nm_ct) {
-    logprint("Error: --make-perm-pheno requires phenotype data.\n");
+    logerrprint("Error: --make-perm-pheno requires phenotype data.\n");
     goto make_perm_pheno_ret_INVALID_CMDLINE;
   }
   g_assoc_thread_ct = MINV(g_thread_ct, permphe_ct);
@@ -11753,7 +11751,7 @@ int32_t make_perm_pheno(pthread_t* threads, char* outname, char* outname_end, ui
 	goto make_perm_pheno_ret_1;
       }
       if (!g_cluster_ct) {
-        logprint("Error: Degenerate --make-perm-pheno invocation (no size 2+ clusters).\n");
+        logerrprint("Error: Degenerate --make-perm-pheno invocation (no size 2+ clusters).\n");
         goto make_perm_pheno_ret_INVALID_CMDLINE;
       }
       retval = cluster_alloc_and_populate_magic_nums(g_cluster_ct, g_cluster_map, g_cluster_starts, &g_tot_quotients, &g_totq_magics, &g_totq_preshifts, &g_totq_postshifts, &g_totq_incrs);
@@ -11791,7 +11789,7 @@ int32_t make_perm_pheno(pthread_t* threads, char* outname, char* outname_end, ui
 	goto make_perm_pheno_ret_1;
       }
       if (!g_cluster_ct) {
-        logprint("Error: Degenerate --make-perm-pheno invocation (no size 2+ clusters).\n");
+        logerrprint("Error: Degenerate --make-perm-pheno invocation (no size 2+ clusters).\n");
         goto make_perm_pheno_ret_INVALID_CMDLINE;
       }
       if (wkspace_alloc_ui_checked(&g_sample_to_cluster, pheno_nm_ct * sizeof(int32_t)) ||
@@ -11900,7 +11898,7 @@ int32_t cluster_assoc_init(const char* flag_name, uintptr_t unfiltered_sample_ct
   uint32_t ujj;
   uint32_t ukk;
   if (cluster_ct < 2) {
-    LOGPRINTF("Error: %s requires at least two valid clusters.\n", flag_name);
+    LOGERRPRINTF("Error: %s requires at least two valid clusters.\n", flag_name);
     return RET_INVALID_CMDLINE;
   }
   // 1. Identify clusters with at least one case and one control, and create
@@ -11993,11 +11991,11 @@ int32_t cluster_assoc_init(const char* flag_name, uintptr_t unfiltered_sample_ct
     return RET_NOMEM;
   }
   if (cluster_ct2 < 2) {
-    LOGPRINTF("Error: %s requires at least two valid clusters.\n", flag_name);
+    LOGERRPRINTF("Error: %s requires at least two valid clusters.\n", flag_name);
     return RET_INVALID_CMDLINE;
   } else if (sample_ct >= 0x40000000) {
     // silly, but I'll document this
-    LOGPRINTF("Error: %s does not support >= 2^30 samples.\n", flag_name);
+    LOGERRPRINTF("Error: %s does not support >= 2^30 samples.\n", flag_name);
     return RET_INVALID_CMDLINE;
   }
   LOGPRINTF("%s: %u valid clusters, with a total of %u cases and %u controls.\n", flag_name, cluster_ct2, case_ct_total, sample_ct - case_ct_total);
@@ -12210,7 +12208,7 @@ int32_t cmh_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char*
     goto cmh_assoc_ret_1;
   }
   if (breslow_day && (cluster_ct2 > 10) && (!perm_bd)) {
-    logprint("Warning: Breslow-Day statistics are unreliable with a large number of small\nclusters.  You may want to look at empirical p-values from the 'perm-bd'\nadaptive permutation test.\n");
+    logerrprint("Warning: Breslow-Day statistics are unreliable with a large number of small\nclusters.  You may want to look at empirical p-values from the 'perm-bd'\nadaptive permutation test.\n");
   }
 
   memcpy(outname_end, ".cmh", 5);
@@ -12431,7 +12429,7 @@ int32_t cmh_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char*
   }
 
   if (cmh_modifier & (CLUSTER_CMH_PERM | CLUSTER_CMH_MPERM)) {
-    logprint("Error: --mh/--bd permutation tests are currently under development.\n");
+    logerrprint("Error: --mh/--bd permutation tests are currently under development.\n");
     goto cmh_assoc_ret_INVALID_CMDLINE;
   }
 
@@ -12551,7 +12549,7 @@ int32_t cmh2_assoc(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
     case_ct += cluster_pheno_gtots[4 * cluster_idx + 2];
   }
   if ((ctrl_ct < 2) || (case_ct < 2)) {
-    logprint("Error: --mh2 requires at least two cases and two controls.\n");
+    logerrprint("Error: --mh2 requires at least two cases and two controls.\n");
     goto cmh2_assoc_ret_INVALID_CMDLINE;
   }
 #ifdef __LP64__
@@ -12560,7 +12558,7 @@ int32_t cmh2_assoc(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
     // routine has an integer overflow here
     // (if/when we do permit this, will need to switch a few variables to type
     // uintptr_t)
-    logprint("Error: --mh2 does not currently support more than 46341 clusters.\n");
+    logerrprint("Error: --mh2 does not currently support more than 46341 clusters.\n");
     goto cmh2_assoc_ret_INVALID_CMDLINE;
   }
 #endif
@@ -12828,7 +12826,7 @@ int32_t homog_assoc(FILE* bedfile, uintptr_t bed_offset, char* outname, char* ou
     goto homog_assoc_ret_NOMEM;
   }
   if (cluster_ct2 > 10) {
-    logprint("Warning: --homog statistics can be unreliable with small clusters.\n");
+    logerrprint("Warning: --homog statistics can be unreliable with small clusters.\n");
   }
 
   memcpy(outname_end, ".homog", 7);
diff --git a/plink_assoc.h b/plink_assoc.h
index 3d9c7e8..476e667 100644
--- a/plink_assoc.h
+++ b/plink_assoc.h
@@ -20,6 +20,8 @@ void generate_cc_cluster_perm_vec(uint32_t tot_ct, uintptr_t* preimage, uint32_t
 
 // void generate_cc_cluster_perm1(uint32_t tot_ct, uintptr_t* preimage, uint32_t cluster_ct, uint32_t* cluster_map, uint32_t* cluster_starts, uint32_t* cluster_case_cts, uint32_t* tot_quotients, uint64_t* totq_magics, uint32_t* totq_preshifts, uint32_t* totq_postshifts, uint32_t* totq_incrs, uintptr_t* perm_vec, sfmt_t* sfmtp);
 
+void transpose_perm1s(uintptr_t* perm_vecs, uint32_t perm_vec_ct, uint32_t pheno_nm_ct, uint32_t* perm_vecst);
+
 int32_t model_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outname, char* outname_end, uint32_t model_modifier, uint32_t model_cell_ct, uint32_t model_mperm_val, double ci_size, double ci_zt, double pfilter, double output_min_p, uint32_t mtest_adjust, double adjust_lambda, uintptr_t unfiltered_marker_ct, uintptr_t* marker_exclude_orig, uintptr_t marker_ct_orig, char* marker_ids, uintptr_t max_marker_id_len, uint32_t plink_maxsnp, uint32_t* marker_pos, char** marke [...]
 
 int32_t qassoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outname, char* outname_end, uint32_t model_modifier, uint32_t model_mperm_val, double pfilter, double output_min_p, uint32_t mtest_adjust, double adjust_lambda, uintptr_t unfiltered_marker_ct, uintptr_t* marker_exclude_orig, uintptr_t marker_ct_orig, char* marker_ids, uintptr_t max_marker_id_len, uint32_t plink_maxsnp, uint32_t* marker_pos, char** marker_allele_ptrs, uintptr_t* marker_reverse, Chrom_info* chrom [...]
diff --git a/plink_calc.c b/plink_calc.c
index 174f005..48e81dc 100644
--- a/plink_calc.c
+++ b/plink_calc.c
@@ -2481,7 +2481,7 @@ int32_t unrelated_herit_batch(uint32_t load_grm_bin, char* grmname, char* phenon
     goto unrelated_herit_batch_ret_READ_FAIL;
   }
   if (unfiltered_sample_ct < 2) {
-    logprint("Error: Less than two samples in .grm.id file.\n");
+    logerrprint("Error: Less than two samples in .grm.id file.\n");
     goto unrelated_herit_batch_ret_INVALID_FORMAT;
   }
   rewind(infile);
@@ -2537,12 +2537,12 @@ int32_t unrelated_herit_batch(uint32_t load_grm_bin, char* grmname, char* phenon
     goto unrelated_herit_batch_ret_1;
   }
   if (!pheno_d) {
-    logprint("Error: --unrelated-heritability requires scalar phenotype.\n");
+    logerrprint("Error: --unrelated-heritability requires scalar phenotype.\n");
     goto unrelated_herit_batch_ret_INVALID_CMDLINE;
   }
   pheno_nm_ct = popcount_longs(pheno_nm, unfiltered_sample_ctl);
   if (pheno_nm_ct < 2) {
-    logprint("Error: Less than two phenotypes present.\n");
+    logerrprint("Error: Less than two phenotypes present.\n");
     goto unrelated_herit_batch_ret_INVALID_FORMAT;
   }
   ulii = CACHEALIGN_DBL(pheno_nm_ct * pheno_nm_ct);
@@ -2665,11 +2665,11 @@ int32_t unrelated_herit_batch(uint32_t load_grm_bin, char* grmname, char* phenon
     retval = RET_INVALID_CMDLINE;
     break;
   unrelated_herit_batch_ret_INVALID_FORMAT_3:
-    logprint("Error: Invalid .grm.gz file.\n");
+    logerrprint("Error: Invalid .grm.gz file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   unrelated_herit_batch_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   unrelated_herit_batch_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -2739,10 +2739,10 @@ int32_t ibs_test_calc(pthread_t* threads, char* read_dists_fname, uintptr_t unfi
   g_sample_ct = sample_ct;
   perm_ct += 1; // first permutation = original config
   if (pheno_ctrl_ct < 2) {
-    logprint("Warning: Skipping --ibs-test due to too few controls (minimum 2).\n");
+    logerrprint("Warning: Skipping --ibs-test due to too few controls (minimum 2).\n");
     goto ibs_test_calc_ret_1;
   } else if (case_ct < 2) {
-    logprint("Warning: Skipping --ibs-test due to too few cases (minimum 2).\n");
+    logerrprint("Warning: Skipping --ibs-test due to too few cases (minimum 2).\n");
     goto ibs_test_calc_ret_1;
   }
   for (ulii = 0; ulii < 6; ulii++) {
@@ -2935,12 +2935,12 @@ int32_t groupdist_calc(pthread_t* threads, uint32_t unfiltered_sample_ct, uintpt
   double dww;
   uint32_t is_case;
   if (pheno_ctrl_ct < 2) {
-    logprint("Warning: Skipping --groupdist due to too few controls (minimum 2).\n");
+    logerrprint("Warning: Skipping --groupdist due to too few controls (minimum 2).\n");
     goto groupdist_calc_ret_1;
   }
   g_case_ct = pheno_nm_ct - pheno_ctrl_ct;
   if (g_case_ct < 2) {
-    logprint("Warning: Skipping --groupdist due to too few cases (minimum 2).\n");
+    logerrprint("Warning: Skipping --groupdist due to too few cases (minimum 2).\n");
     goto groupdist_calc_ret_1;
   }
   g_ctrl_ct = pheno_ctrl_ct;
@@ -3277,18 +3277,18 @@ int32_t calc_regress_pcs(char* evecname, uint32_t regress_pcs_modifier, uint32_t
   tbuf[MAXLINELEN - 1] = ' ';
   if (!fgets(tbuf, MAXLINELEN - 6, evecfile)) {
     if (feof(evecfile)) {
-      goto calc_regress_pcs_ret_INVALID_FORMAT;
+      goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
     } else {
       goto calc_regress_pcs_ret_READ_FAIL;
     }
   }
   if (!tbuf[MAXLINELEN - 7]) {
-    logprint("Error: Excessively long line in .evec/.eigenvec file.\n");
-    goto calc_regress_pcs_ret_INVALID_FORMAT2;
+    logerrprint("Error: Excessively long line in .evec/.eigenvec file.\n");
+    goto calc_regress_pcs_ret_INVALID_FORMAT;
   }
   bufptr = skip_initial_spaces(tbuf);
   if (no_more_tokens_kns(bufptr)) {
-    goto calc_regress_pcs_ret_INVALID_FORMAT;
+    goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
   }
   if (memcmp(bufptr, "#eigvals:", 9)) {
     is_eigenvec = 1;
@@ -3300,7 +3300,7 @@ int32_t calc_regress_pcs(char* evecname, uint32_t regress_pcs_modifier, uint32_t
     bufptr = next_token(bufptr);
   }
   if (!pc_ct) {
-    goto calc_regress_pcs_ret_INVALID_FORMAT;
+    goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
   }
   if (pc_ct > max_pcs) {
     sprintf(logbuf, "%svec format detected.  Regressing on %d PC%s (out of %d).\n", is_eigenvec? "GCTA .eigen" : "SMARTPCA .e", max_pcs, (max_pcs == 1)? "" : "s", pc_ct);
@@ -3333,10 +3333,10 @@ int32_t calc_regress_pcs(char* evecname, uint32_t regress_pcs_modifier, uint32_t
       bufptr = next_token_mult(skip_initial_spaces(tbuf), 2);
       for (uii = 0; uii < pc_ct; uii++) {
 	if (no_more_tokens_kns(bufptr)) {
-	  goto calc_regress_pcs_ret_INVALID_FORMAT;
+	  goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
 	}
 	if (scan_double(bufptr, &(pc_matrix[uii * sample_ct + sample_idx]))) {
-	  goto calc_regress_pcs_ret_INVALID_FORMAT;
+	  goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
 	}
 	bufptr = next_token(bufptr);
       }
@@ -3366,10 +3366,10 @@ int32_t calc_regress_pcs(char* evecname, uint32_t regress_pcs_modifier, uint32_t
       bufptr = next_token(skip_initial_spaces(tbuf));
       for (uii = 0; uii < pc_ct; uii++) {
 	if (no_more_tokens_kns(bufptr)) {
-	  goto calc_regress_pcs_ret_INVALID_FORMAT;
+	  goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
 	}
 	if (scan_double(bufptr, &(pc_matrix[uii * sample_ct + sample_idx]))) {
-	  goto calc_regress_pcs_ret_INVALID_FORMAT;
+	  goto calc_regress_pcs_ret_INVALID_FORMAT_2G;
 	}
 	bufptr = next_token(bufptr);
       }
@@ -3642,12 +3642,12 @@ int32_t calc_regress_pcs(char* evecname, uint32_t regress_pcs_modifier, uint32_t
     retval = RET_WRITE_FAIL;
     break;
   calc_regress_pcs_ret_INVALID_FORMAT_3:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
+  calc_regress_pcs_ret_INVALID_FORMAT_2G:
+    logerrprint("Error: Improperly formatted .evec file.\n");
   calc_regress_pcs_ret_INVALID_FORMAT:
-    logprint("Error: Improperly formatted .evec file.\n");
-  calc_regress_pcs_ret_INVALID_FORMAT2:
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -5040,7 +5040,7 @@ int32_t calc_genome(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, uin
   uint32_t pct;
 
   if (is_set(chrom_info_ptr->haploid_mask, 0)) {
-    logprint("Error: --genome cannot be used on haploid genomes.\n");
+    logerrprint("Error: --genome cannot be used on haploid genomes.\n");
     goto calc_genome_ret_INVALID_CMDLINE;
   }
   g_sample_ct = sample_ct;
@@ -5898,7 +5898,7 @@ int32_t rel_cutoff_batch(uint32_t load_grm_bin, char* grmname, char* outname, ch
   int32_t cur_prune;
   ulii = (uintptr_t)(grmname_end - grmname);
   if ((ulii == (uintptr_t)(outname_end - outname)) && (!memcmp(grmname, outname, ulii))) {
-    LOGPRINTF("Error: --rel-cutoff input and output ID filenames cannot match.%s\n", strcmp(outname, PROG_NAME_STR)? "" : "  (Use --out.)");
+    LOGERRPRINTF("Error: --rel-cutoff input and output ID filenames cannot match.%s\n", strcmp(outname, PROG_NAME_STR)? "" : "  (Use --out.)");
     goto rel_cutoff_batch_ret_INVALID_CMDLINE;
   }
   memcpy(grmname_end, ".grm.id", 8);
@@ -6405,12 +6405,12 @@ int32_t rel_cutoff_batch(uint32_t load_grm_bin, char* grmname, char* outname, ch
     retval = RET_WRITE_FAIL;
     break;
   rel_cutoff_batch_ret_INVALID_FORMAT_GENERIC:
-    putchar('\n');
-    logprint("Error: Improperly formatted .grm.gz file.\n");
+    logprint("\n");
+    logerrprint("Error: Improperly formatted .grm.gz file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   rel_cutoff_batch_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   rel_cutoff_batch_ret_INVALID_CMDLINE:
@@ -6765,7 +6765,7 @@ int32_t load_distance_wts(char* distance_wts_fname, uintptr_t unfiltered_marker_
   wkspace_reset(wkspace_mark);
   marker_ct = popcount_longs(marker_include, unfiltered_marker_ctl) - zcount;
   if (!marker_ct) {
-    logprint("Error: No valid nonzero entries in --distance-wts file.\n");
+    logerrprint("Error: No valid nonzero entries in --distance-wts file.\n");
     goto load_distance_wts_ret_INVALID_FORMAT;
   }
   wkspace_left -= topsize;
@@ -6808,7 +6808,7 @@ int32_t load_distance_wts(char* distance_wts_fname, uintptr_t unfiltered_marker_
   load_distance_wts_ret_INVALID_WEIGHT:
     sprintf(logbuf, "Error: Invalid weight on line %" PRIuPTR " of --distance-wts file.\n", line_idx);
   load_distance_wts_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_distance_wts_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -6880,7 +6880,7 @@ int32_t calc_rel(pthread_t* threads, uint32_t parallel_idx, uint32_t parallel_to
   uint32_t* giptr2;
   uintptr_t* glptr2;
   if (distance_wts_fname) {
-    logprint("Error: --make-{rel,grm-gz,grm-bin} + --distance-wts is currently under\ndevelopment.\n");
+    logerrprint("Error: --make-{rel,grm-gz,grm-bin} + --distance-wts is currently under\ndevelopment.\n");
     goto calc_rel_ret_1;
   }
 
@@ -7410,10 +7410,10 @@ int32_t calc_rel(pthread_t* threads, uint32_t parallel_idx, uint32_t parallel_to
     // even if this isn't true (empty intersection, or inaccurate 0 MAF), but
     // at least this catches the common case
     if (calculation_type & (CALC_REGRESS_REL | CALC_PCA | CALC_UNRELATED_HERITABILITY)) {
-      logprint("Error: Sample(s) present with no genotype data.  Use --mind to filter them out.\n");
+      logerrprint("Error: Sample(s) present with no genotype data.  Use --mind to filter them out.\n");
       retval = RET_INVALID_FORMAT;
     } else {
-      logprint("Warning: Sample(s) present with no genotype data.  Use --mind to filter them\nout.\n");
+      logerrprint("Warning: Sample(s) present with no genotype data.  Use --mind to filter them\nout.\n");
     }
   }
   while (0) {
@@ -7525,7 +7525,7 @@ int32_t calc_pca(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outna
       pc_ct = marker_ct;
       sprintf(logbuf, "Warning: calculating %u PCs, since there are only %u autosomal markers.\n", pc_ct, pc_ct);
     }
-    logprintb();
+    logerrprintb();
   }
   ulii = (pca_sample_ct * (pca_sample_ct + 1)) / 2;
   if (ulii < sample_ct * pc_ct) {
@@ -8802,13 +8802,13 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
   if (sample_ct <= cp->min_ct) {
     // todo: check if anyone uses --mds-plot without caring about clustering;
     // if yes, we should stop forcing --mds-plot to be used with --cluster.
-    logprint("Error: --K parameter too large (>= sample count).\n");
+    logerrprint("Error: --K parameter too large (>= sample count).\n");
     goto calc_cluster_neighbor_ret_INVALID_CMDLINE;
   }
   if (cluster_ct) {
     cur_cluster_ct += cluster_ct - cluster_starts[cluster_ct];
     if (cur_cluster_ct <= cp->min_ct) {
-      logprint("Error: --K parameter too large (>= initial cluster count).\n");
+      logerrprint("Error: --K parameter too large (>= initial cluster count).\n");
       goto calc_cluster_neighbor_ret_INVALID_CMDLINE;
     }
     sample_to_cluster = (uint32_t*)malloc(sample_ct * sizeof(int32_t));
@@ -8842,7 +8842,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
   }
   if (do_neighbor) {
     if (neighbor_n2 >= sample_ct) {
-      logprint("Error: Second --neighbour parameter too large (>= population size).\n");
+      logerrprint("Error: Second --neighbour parameter too large (>= population size).\n");
       goto calc_cluster_neighbor_ret_INVALID_CMDLINE;
     }
     ulii = neighbor_n2 * sample_ct;
@@ -8939,7 +8939,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
 		if (clidx1 != clidx2) {
 		  SET_BIT(cluster_merge_prevented, tcoord);
 		} else if (!ppc_warning) {
-		  logprint("Warning: Initial cluster assignment violates PPC test constraint.\n");
+		  logerrprint("Warning: Initial cluster assignment violates PPC test constraint.\n");
 		  ppc_warning = 1;
 		}
 	      }
@@ -9273,7 +9273,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
       putchar('\r');
       LOGPRINTFWW("IBM matrix written to %s .\n", outname);
       if (ibm_warning) {
-	logprint("Warning: Initial cluster assignment violates IBM constraint.\n");
+	logerrprint("Warning: Initial cluster assignment violates IBM constraint.\n");
       }
       if (mds_fill_nonclust && (!cluster_ct)) {
 	memcpy(mds_plot_dmatrix_copy, cluster_sorted_ibs, initial_triangle_size * sizeof(double));
@@ -9347,7 +9347,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
       }
     }
     if (mc_warning) {
-      logprint("Warning: Initial cluster assignment violates --mc restriction.\n");
+      logerrprint("Warning: Initial cluster assignment violates --mc restriction.\n");
     }
     for (clidx1 = cluster_ct; clidx1 < cur_cluster_ct; clidx1++) {
       cur_cluster_sizes[clidx1] = 1;
@@ -9440,7 +9440,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
 	}
       }
       if (mcc_warning) {
-        logprint("Warning: Initial cluster assignment violates --mcc restriction.\n");
+        logerrprint("Warning: Initial cluster assignment violates --mcc restriction.\n");
       }
     }
   }
@@ -9499,7 +9499,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
       heap_size -= popcount_longs_nzbase(cluster_merge_prevented, tcoord / BITCT, (initial_triangle_size + (BITCT - 1)) / BITCT);
     }
     if (!heap_size) {
-      logprint("Error: No cluster merges possible.\n");
+      logerrprint("Error: No cluster merges possible.\n");
       goto calc_cluster_neighbor_ret_INVALID_CMDLINE;
     }
     wkspace_reset(cluster_sorted_ibs);
@@ -9603,7 +9603,7 @@ int32_t calc_cluster_neighbor(pthread_t* threads, FILE* bedfile, uintptr_t bed_o
     }
 #ifdef __LP64__
   } else {
-    logprint("Error: --cluster cannot handle >65536 initial clusters yet.\n");
+    logerrprint("Error: --cluster cannot handle >65536 initial clusters yet.\n");
     retval = RET_CALC_NOT_YET_SUPPORTED;
     goto calc_cluster_neighbor_ret_1;
   }
diff --git a/plink_cluster.c b/plink_cluster.c
index 5a036c2..d1cf1d8 100644
--- a/plink_cluster.c
+++ b/plink_cluster.c
@@ -117,7 +117,7 @@ int32_t load_clusters(char* fname, uintptr_t unfiltered_sample_ct, uintptr_t* sa
 	  cluster_kr_ct++;
 	}
 	if (!cluster_kr_ct) {
-	  logprint("Error: Empty --keep-clusters file.\n");
+	  logerrprint("Error: Empty --keep-clusters file.\n");
 	  goto load_clusters_ret_INVALID_FORMAT;
 	}
       }
@@ -366,7 +366,7 @@ int32_t load_clusters(char* fname, uintptr_t unfiltered_sample_ct, uintptr_t* sa
     }
     if (cluster_names) {
       if (max_cluster_id_len > MAX_ID_LEN_P1) {
-	logprint("Error: Cluster IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+	logerrprint("Error: Cluster IDs are limited to " MAX_ID_LEN_STR " characters.\n");
 	goto load_clusters_ret_INVALID_FORMAT;
       }
       *max_cluster_id_len_ptr = max_cluster_id_len;
@@ -455,10 +455,10 @@ int32_t load_clusters(char* fname, uintptr_t unfiltered_sample_ct, uintptr_t* sa
       LOGPRINTF("--within: %" PRIuPTR " cluster%s loaded, covering a total of %" PRIuPTR " %s.\n", cluster_ct, (cluster_ct == 1)? "" : "s", assigned_ct, species_str(assigned_ct));
     } else {
       if (sorted_keep_ids) {
-	logprint("Error: No samples named in --within file remain in the current analysis, so\n--keep-clusters/--keep-cluster-names excludes everyone.\n");
+	logerrprint("Error: No samples named in --within file remain in the current analysis, so\n--keep-clusters/--keep-cluster-names excludes everyone.\n");
 	goto load_clusters_ret_INVALID_FORMAT;
       }
-      logprint("Warning: No samples named in --within file remain in the current analysis.\n");
+      logerrprint("Warning: No samples named in --within file remain in the current analysis.\n");
       goto load_clusters_ret_1;
     }
   } else {
@@ -471,7 +471,7 @@ int32_t load_clusters(char* fname, uintptr_t unfiltered_sample_ct, uintptr_t* sa
     // 4. initialize other data structures
     if (max_cluster_id_len > MAX_ID_LEN_P1) {
       // max FID len was previously checked
-      logprint("Error: Cluster IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: Cluster IDs are limited to " MAX_ID_LEN_STR " characters.\n");
       goto load_clusters_ret_INVALID_FORMAT;
     }
 
@@ -497,7 +497,7 @@ int32_t load_clusters(char* fname, uintptr_t unfiltered_sample_ct, uintptr_t* sa
       assigned_ct++;
     }
     if (!assigned_ct) {
-      logprint("Error: --keep-clusters/--keep-cluster-names excludes everyone.\n");
+      logerrprint("Error: --keep-clusters/--keep-cluster-names excludes everyone.\n");
       goto load_clusters_ret_INVALID_FORMAT;
     }
     *max_cluster_id_len_ptr = max_cluster_id_len;
@@ -572,7 +572,7 @@ int32_t load_clusters(char* fname, uintptr_t unfiltered_sample_ct, uintptr_t* sa
   load_clusters_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --within file has fewer tokens than expected.\n", line_idx);
   load_clusters_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_clusters_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -787,7 +787,7 @@ int32_t extract_clusters(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
     retval = RET_READ_FAIL;
     break;
   extract_clusters_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1136,7 +1136,7 @@ int32_t read_dists(char* dist_fname, char* id_fname, uintptr_t unfiltered_sample
     }
     fclose_null(&id_file);
     if (matching_entry_ct < sample_ct) {
-      logprint("Error: --read-dists ID file does not contain all samples in current run.\n");
+      logerrprint("Error: --read-dists ID file does not contain all samples in current run.\n");
       goto read_dists_ret_INVALID_FORMAT;
     }
   } else if (cluster_ct) {
@@ -1278,7 +1278,7 @@ int32_t read_dists(char* dist_fname, char* id_fname, uintptr_t unfiltered_sample
     retval = RET_READ_FAIL;
     break;
   read_dists_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   read_dists_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -1354,7 +1354,7 @@ int32_t read_genome(char* read_genome_fname, uintptr_t unfiltered_sample_ct, uin
   } while (is_eoln_kns(*bufptr));
   // a little bit of input validation
   if (memcmp(bufptr, "FID1", 4)) {
-    logprint("Error: Invalid --read-genome file header line.\n");
+    logerrprint("Error: Invalid --read-genome file header line.\n");
     goto read_genome_ret_INVALID_FORMAT;
   }
   while (gzgets(gz_infile, tbuf, MAXLINELEN)) {
@@ -1413,7 +1413,7 @@ int32_t read_genome(char* read_genome_fname, uintptr_t unfiltered_sample_ct, uin
       sample_idx2 = sample_to_cluster[sample_idx2];
       if (sample_idx1 == sample_idx2) {
 	if (ppc_fail && (!ppc_warning)) {
-	  logprint("Warning: Initial cluster assignment violates PPC test constraint.\n");
+	  logerrprint("Warning: Initial cluster assignment violates PPC test constraint.\n");
 	  ppc_warning = 1;
 	}
 	continue;
@@ -1453,13 +1453,13 @@ int32_t read_genome(char* read_genome_fname, uintptr_t unfiltered_sample_ct, uin
     retval = RET_READ_FAIL;
     break;
   read_genome_ret_MISSING_TOKENS:
-    LOGPRINTF("Error: Line %" PRIuPTR " of --read-genome file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of --read-genome file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_genome_ret_LONG_LINE:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --read-genome file is pathologically long.\n", line_idx);
   read_genome_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   read_genome_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -1566,7 +1566,7 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
 	  cc = *bufptr;
 	}
 	if (cov_ct > 65536) {
-          logprint("Error: Too many tokens in --match-type file (max 65536).\n");
+          logerrprint("Error: Too many tokens in --match-type file (max 65536).\n");
 	  goto cluster_enforce_match_ret_INVALID_FORMAT;
 	}
       }
@@ -1575,12 +1575,12 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
       }
       fclose_null(&typefile);
       if (!cov_ct) {
-        logprint("Error: Empty --match-type file.\n");
+        logerrprint("Error: Empty --match-type file.\n");
 	goto cluster_enforce_match_ret_INVALID_FORMAT;
       }
       non_null_cov_ct = cov_ct - cov_idx;
       if (!non_null_cov_ct) {
-	logprint("Error: Degenerate --match-type file (all -1/*).\n");
+	logerrprint("Error: Degenerate --match-type file (all -1/*).\n");
 	goto cluster_enforce_match_ret_INVALID_FORMAT;
       }
       while (!cov_type_arr[cov_ct - 1]) {
@@ -1736,7 +1736,7 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
       }
     }
     if (cluster_mismatch_warning) {
-      logprint("Warning: Initial cluster assignment violates --match constraint.\n");
+      logerrprint("Warning: Initial cluster assignment violates --match constraint.\n");
       cluster_mismatch_warning = 0;
     }
     cov_ct = 0;
@@ -1795,7 +1795,7 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
 	tol_arr[cov_ct++] = dxx;
 	bufptr = skip_initial_spaces(token_endnn(bufptr));
 	if (cov_ct > 65536) {
-          logprint("Error: Too many values in --qt file (max 65536).\n");
+          logerrprint("Error: Too many values in --qt file (max 65536).\n");
 	  goto cluster_enforce_match_ret_INVALID_FORMAT;
 	}
       }
@@ -1805,7 +1805,7 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
     }
     fclose_null(&typefile);
     if (!cov_ct) {
-      logprint("Error: Empty --qt file.\n");
+      logerrprint("Error: Empty --qt file.\n");
       goto cluster_enforce_match_ret_INVALID_FORMAT;
     }
     wkspace_alloc(cov_ct * sizeof(double)); // tol_arr
@@ -1938,7 +1938,7 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
       }
     }
     if (cluster_mismatch_warning) {
-      logprint("Warning: Initial cluster assignment violates --qmatch constraint.\n");
+      logerrprint("Warning: Initial cluster assignment violates --qmatch constraint.\n");
     }
   }
   LOGPRINTF("--%smatch constraints applied.\n", cp->match_fname? (cp->qmatch_fname? "match and q" : "") : "q");
@@ -1953,13 +1953,13 @@ int32_t cluster_enforce_match(Cluster_info* cp, int32_t missing_pheno, uintptr_t
     retval = RET_READ_FAIL;
     break;
   cluster_enforce_match_ret_MISSING_TOKENS_Q:
-    LOGPRINTF("Error: Line %" PRIuPTR " of --qmatch file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of --qmatch file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   cluster_enforce_match_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --match file has fewer tokens than expected.\n", line_idx);
   cluster_enforce_match_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   cluster_enforce_match_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -2955,7 +2955,7 @@ int32_t mds_plot(char* outname, char* outname_end, uintptr_t* sample_exclude, ui
     goto mds_plot_ret_NOMEM;
   }
   if ((sample_ct > 5000) && (!is_mds_cluster) && (final_cluster_ct < sample_ct) && (final_cluster_ct > 1)) {
-    LOGPRINTF("Warning: Per-sample --mds-plot can be very slow with over 5000 %s.\nConsider using the 'by-cluster' modifier.\n", g_species_plural);
+    LOGERRPRINTF("Warning: Per-sample --mds-plot can be very slow with over 5000 %s.\nConsider using the 'by-cluster' modifier.\n", g_species_plural);
   }
   for (clidx1 = 0; clidx1 < cur_cluster_ct; clidx1++) {
     clidx2 = cur_cluster_remap[clidx1];
@@ -3267,7 +3267,7 @@ int32_t mds_plot_eigendecomp(char* outname, char* outname_end, uintptr_t* sample
     goto mds_plot_eigendecomp_ret_NOMEM;
   }
   if ((sample_ct > 5000) && (!is_mds_cluster) && (final_cluster_ct < sample_ct) && (final_cluster_ct > 1)) {
-    LOGPRINTF("Warning: Per-sample --mds-plot can be very slow with over 5000 %s.\nConsider using the 'by-cluster' modifier.\n", g_species_plural);
+    LOGERRPRINTF("Warning: Per-sample --mds-plot can be very slow with over 5000 %s.\nConsider using the 'by-cluster' modifier.\n", g_species_plural);
   }
   for (clidx1 = 0; clidx1 < cur_cluster_ct; clidx1++) {
     clidx2 = cur_cluster_remap[clidx1];
diff --git a/plink_cnv.c b/plink_cnv.c
index f988695..95e5c3b 100644
--- a/plink_cnv.c
+++ b/plink_cnv.c
@@ -10,7 +10,7 @@ int32_t cnv_subset_load(char* subset_fname, char** subset_list_ptr, uintptr_t* s
     goto cnv_subset_load_ret_1;
   }
   if (!subset_ct) {
-    logprint("Error: Empty --cnv-subset file.\n");
+    logerrprint("Error: Empty --cnv-subset file.\n");
     goto cnv_subset_load_ret_INVALID_FORMAT;
   }
 #ifndef __LP64__
@@ -147,7 +147,7 @@ int32_t cnv_intersect_load(uint32_t intersect_filter_type, char* intersect_filte
       }
       if (ii < jj) {
 	if (!reverse_warning_ct) {
-	  LOGPRINTF("Warning: End of range before start of range on line %" PRIuPTR " of\n%s.\n", line_idx, cift_str);
+	  LOGERRPRINTF("Warning: End of range before start of range on line %" PRIuPTR " of\n%s.\n", line_idx, cift_str);
 	}
 	reverse_warning_ct++;
 	kk = ii;
@@ -193,7 +193,7 @@ int32_t cnv_intersect_load(uint32_t intersect_filter_type, char* intersect_filte
     if (subset_ct) {
       fill_ulong_zero(il_chrom_start_small, chrom_code_end + 1);
       fill_ulong_zero(il_chrom_start_large, chrom_code_end + 1);
-      logprint("Warning: All intervals filtered out by --cnv-subset.\n");
+      logerrprint("Warning: All intervals filtered out by --cnv-subset.\n");
       goto cnv_intersect_load_ret_1;
     }
     sprintf(logbuf, "Error: Empty %s.\n", cift_str);
@@ -301,7 +301,7 @@ int32_t cnv_intersect_load(uint32_t intersect_filter_type, char* intersect_filte
     retval = RET_READ_FAIL;
     break;
   cnv_intersect_load_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -322,7 +322,8 @@ int32_t cnv_first_nonheader_line(FILE* cnvfile, uintptr_t* line_idx_ptr) {
       goto cnv_first_nonheader_line_fgets_fail;
     }
     if (!tbuf[MAXLINELEN - 1]) {
-      LOGPRINTF("\nError: Line %" PRIuPTR " of .cnv file is pathologically long.\n", line_idx);
+      logprint("\n");
+      LOGERRPRINTF("Error: Line %" PRIuPTR " of .cnv file is pathologically long.\n", line_idx);
       goto cnv_first_nonheader_line_ret_INVALID_FORMAT;
     }
     bufptr = skip_initial_spaces(tbuf);
@@ -340,7 +341,8 @@ int32_t cnv_first_nonheader_line(FILE* cnvfile, uintptr_t* line_idx_ptr) {
       retval = RET_READ_FAIL;
       break;
     }
-    logprint("\nError: Empty .cnv file.\n");
+    logprint("\n");
+    logerrprint("Error: Empty .cnv file.\n");
     // fall through
   cnv_first_nonheader_line_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
@@ -496,8 +498,8 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
   do {
     line_idx++;
     if (!tbuf[MAXLINELEN - 1]) {
-      sprintf(logbuf, "\nError: Line %" PRIuPTR " of .cnv file is pathologically long.\n", line_idx);
-      goto cnv_make_map_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Line %" PRIuPTR " of .cnv file is pathologically long.\n", line_idx);
+      goto cnv_make_map_ret_INVALID_FORMAT_2N;
     }
     bufptr = skip_initial_spaces(tbuf);
     if (!is_eoln_kns(*bufptr)) {
@@ -505,14 +507,14 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
       bufptr = next_token_mult(bufptr, 2);
       bufptr2 = next_token_mult(bufptr, req_fields);
       if (no_more_tokens_kns(bufptr2)) {
-	sprintf(logbuf, "\nError: Line %" PRIuPTR " of .cnv file has fewer tokens than expected.\n", line_idx);
-	goto cnv_make_map_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Line %" PRIuPTR " of .cnv file has fewer tokens than expected.\n", line_idx);
+	goto cnv_make_map_ret_INVALID_FORMAT_2N;
       }
       ii = get_chrom_code(chrom_info_ptr, bufptr);
       if (ii < 0) {
 	if ((!allow_extra_chroms) || (ii == -1)) {
-	  sprintf(logbuf, "\nError: Invalid chromosome code on line %" PRIuPTR " of .cnv file.\n", line_idx);
-          goto cnv_make_map_ret_INVALID_FORMAT_2;
+	  sprintf(logbuf, "Error: Invalid chromosome code on line %" PRIuPTR " of .cnv file.\n", line_idx);
+          goto cnv_make_map_ret_INVALID_FORMAT_2N;
 	}
         retval = resolve_or_add_chrom_name(chrom_info_ptr, bufptr, &ii, line_idx, ".cnv file");
 	if (retval) {
@@ -527,12 +529,12 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
       bufptr2 = next_token(bufptr);
       bufptr = next_token(bufptr2);
       if (scan_uint_defcap(bufptr2, &seg_start) || scan_uint_defcap(bufptr, &seg_end)) {
-	sprintf(logbuf, "\nError: Invalid bp coordinate on line %" PRIuPTR " of .cnv file.\n", line_idx);
-        goto cnv_make_map_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid bp coordinate on line %" PRIuPTR " of .cnv file.\n", line_idx);
+        goto cnv_make_map_ret_INVALID_FORMAT_2N;
       }
       if (seg_end < seg_start) {
-	sprintf(logbuf, "\nError: Segment end coordinate smaller than segment start on line %" PRIuPTR " of\n.cnv file.\n", line_idx);
-	goto cnv_make_map_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Segment end coordinate smaller than segment start on line %" PRIuPTR " of\n.cnv file.\n", line_idx);
+	goto cnv_make_map_ret_INVALID_FORMAT_2N;
       }
       if ((marker_pos_start > (int32_t)seg_start) || ((marker_pos_end != -1) && (marker_pos_end < (int32_t)seg_end))) {
 	continue;
@@ -548,8 +550,8 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
       if (cnv_calc_type & (CNV_DEL | CNV_DUP)) {
 	bufptr2 = next_token(bufptr);
 	if (scan_uint_defcap(bufptr2, (uint32_t*)&ii)) {
-	  sprintf(logbuf, "\nError: Invalid variant copy count on line %" PRIuPTR " of .cnv file.\n", line_idx);
-	  goto cnv_make_map_ret_INVALID_FORMAT_2;
+	  sprintf(logbuf, "Error: Invalid variant copy count on line %" PRIuPTR " of .cnv file.\n", line_idx);
+	  goto cnv_make_map_ret_INVALID_FORMAT_2N;
 	}
 	if (cnv_del) {
 	  if (ii > 1) {
@@ -562,8 +564,8 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
       if (filter_score) {
 	bufptr2 = next_token_mult(bufptr, 2);
 	if (scan_double(bufptr2, &dxx)) {
-          sprintf(logbuf, "\nError: Invalid confidence score on line %" PRIuPTR " of .cnv file.\n", line_idx);
-	  goto cnv_make_map_ret_INVALID_FORMAT_2;
+          sprintf(logbuf, "Error: Invalid confidence score on line %" PRIuPTR " of .cnv file.\n", line_idx);
+	  goto cnv_make_map_ret_INVALID_FORMAT_2N;
 	}
 	if ((dxx < min_score) || (dxx > max_score)) {
 	  continue;
@@ -572,8 +574,8 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
       if (filter_sites) {
 	bufptr2 = next_token_mult(bufptr, 3);
 	if (scan_posint_defcap(bufptr2, (uint32_t*)&ii)) {
-	  sprintf(logbuf, "\nError: Invalid probe count on line %" PRIuPTR " of .cnv file.\n", line_idx);
-	  goto cnv_make_map_ret_INVALID_FORMAT_2;
+	  sprintf(logbuf, "Error: Invalid probe count on line %" PRIuPTR " of .cnv file.\n", line_idx);
+	  goto cnv_make_map_ret_INVALID_FORMAT_2N;
 	}
 	if ((((uint32_t)ii) < min_sites) || (((uint32_t)ii) > max_sites)) {
 	  continue;
@@ -604,11 +606,8 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
     goto cnv_make_map_ret_READ_FAIL;
   }
   if (!raw_marker_ct) {
-    if (cnv_calc_type) {
-      logprint("\nError: No variants.\n");
-    } else {
-      logprint("\nError: No variants after filtering.\n");
-    }
+    logprint("\n");
+    logerrprint(cnv_calc_type? "Error: No variants.\n" : "Error: No variants after filtering.\n");
     goto cnv_make_map_ret_INVALID_FORMAT;
   }
 #ifdef __cplusplus
@@ -628,7 +627,8 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
     if (marker_pos_arr[ulii] != llii) {
 #ifdef __LP64__
       if ((++distinct_marker_ct) == 0x80000000U) {
-	logprint("\nError: Too many distinct .cnv.map positions (max 2^31 - 1).\n");
+	logprint("\n");
+	logerrprint("Error: Too many distinct .cnv.map positions (max 2^31 - 1).\n");
 	goto cnv_make_map_ret_INVALID_FORMAT;
       }
 #endif
@@ -665,8 +665,9 @@ int32_t cnv_make_map(FILE* cnvfile, char* new_mapname, uint32_t cnv_calc_type, u
   cnv_make_map_ret_WRITE_FAIL:
     retval = RET_WRITE_FAIL;
     break;
-  cnv_make_map_ret_INVALID_FORMAT_2:
-    logprintb();
+  cnv_make_map_ret_INVALID_FORMAT_2N:
+    logprint("\n");
+    logerrprintb();
   cnv_make_map_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -705,7 +706,7 @@ int32_t validate_cnv_map(FILE** mapfile_ptr, char* mapname, int32_t* marker_pos_
     line_idx++;
     if (!fgets(tbuf, MAXLINELEN, *mapfile_ptr)) {
       if (feof(*mapfile_ptr)) {
-	logprint("Error: Empty .cnv.map file.\n");
+	logerrprint("Error: Empty .cnv.map file.\n");
 	goto validate_cnv_map_ret_INVALID_FORMAT;
       } else {
 	goto validate_cnv_map_ret_READ_FAIL;
@@ -785,7 +786,7 @@ int32_t validate_cnv_map(FILE** mapfile_ptr, char* mapname, int32_t* marker_pos_
       max_marker_id_len = cur_marker_id_len + 1;
     }
     if (++marker_ct == 0x80000000U) {
-      logprint("Error: Too many entries in .cnv.map file (max 2147483647).\n");
+      logerrprint("Error: Too many entries in .cnv.map file (max 2147483647).\n");
       goto validate_cnv_map_ret_INVALID_FORMAT;
     }
   } while (fgets(tbuf, MAXLINELEN, *mapfile_ptr));
@@ -805,17 +806,17 @@ int32_t validate_cnv_map(FILE** mapfile_ptr, char* mapname, int32_t* marker_pos_
     retval = RET_READ_FAIL;
     break;
   validate_cnv_map_ret_UNSORTED:
-    logprint("Error: Unsorted .cnv.map file.\n");
+    logerrprint("Error: Unsorted .cnv.map file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   validate_cnv_map_ret_MISSING_TOKENS:
-    LOGPRINTF("Error: Line %" PRIuPTR " of .cnv.map file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .cnv.map file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   validate_cnv_map_ret_LONG_LINE:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of .cnv.map is pathologically long.\n", line_idx);
   validate_cnv_map_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   validate_cnv_map_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -947,14 +948,14 @@ int32_t plink_cnv(char* outname, char* outname_end, char* cnvname, char* mapname
       if (mapname[0] == '\0') {
         uii = strlen(cnvname);
         if ((uii < 5) || (cnvname[uii - 4] != '.') || (!match_upper_nt(&(cnvname[uii - 3]), "CNV", 3))) {
-	  logprint("Error: No .cnv.map filename specified, and .cnv filename does not unambiguously\nspecify how an autogenerated file should be named.  Use --cnv-make-map + --out.\n");
+	  logerrprint("Error: No .cnv.map filename specified, and .cnv filename does not unambiguously\nspecify how an autogenerated file should be named.  Use --cnv-make-map + --out.\n");
 	  goto plink_cnv_ret_INVALID_CMDLINE;
 	}
 	memcpy(mapname, cnvname, uii);
 	memcpy(&(mapname[uii]), ".map", 5);
 	sptr = &(mapname[uii + 4]);
 	if (filename_exists(mapname, sptr, "")) {
-	  LOGPRINTFWW("Error: No .cnv.map filename specified, and natural autogeneration target\n(%s) already exists.\n", mapname);
+	  LOGERRPRINTFWW("Error: No .cnv.map filename specified, and natural autogeneration target\n(%s) already exists.\n", mapname);
 	  goto plink_cnv_ret_INVALID_CMDLINE;
 	}
       }
@@ -1021,6 +1022,6 @@ int32_t plink_cnv(char* outname, char* outname_end, char* cnvname, char* mapname
 #endif // HIGH_MAX_CHROM
 
 int32_t plink_gvar(char* outname, char* outname_end, char* gvarname, char* mapname, char* famname) {
-  logprint("Error: Common CNP analysis not yet supported.\n");
+  logerrprint("Error: Common CNP analysis not yet supported.\n");
   return RET_CALC_NOT_YET_SUPPORTED;
 }
diff --git a/plink_common.c b/plink_common.c
index 22cb061..f27d740 100644
--- a/plink_common.c
+++ b/plink_common.c
@@ -74,17 +74,21 @@ void logstr(const char* ss) {
   if (!g_debug_on) {
     fputs(ss, logfile);
     if (ferror(logfile)) {
-      printf("\nWarning: Logging failure on:\n%s\nFurther logging will not be attempted in this run.\n", ss);
+      putchar('\n');
+      fflush(stdout);
+      fprintf(stderr, "Warning: Logging failure on:\n%s\nFurther logging will not be attempted in this run.\n", ss);
       g_log_failed = 1;
     }
   } else {
     if (g_log_failed) {
-      fputs(ss, stdout);
       fflush(stdout);
+      fputs(ss, stderr);
     } else {
       fputs(ss, logfile);
       if (ferror(logfile)) {
-        printf("\nError: Debug logging failure.  Dumping to standard output:\n%s", ss);
+	putchar('\n');
+	fflush(stdout);
+        fprintf(stderr, "Error: Debug logging failure.  Dumping to stderr:\n%s", ss);
 	g_log_failed = 1;
       } else {
 	fflush(logfile);
@@ -98,11 +102,23 @@ void logprint(const char* ss) {
   fputs(ss, stdout);
 }
 
+void logerrprint(const char* ss) {
+  logstr(ss);
+  fflush(stdout);
+  fputs(ss, stderr);
+}
+
 void logprintb() {
   logstr(logbuf);
   fputs(logbuf, stdout);
 }
 
+void logerrprintb() {
+  logstr(logbuf);
+  fflush(stdout);
+  fputs(logbuf, stderr);
+}
+
 void wordwrap(char* ss, uint32_t suffix_len) {
   // This should have been written eons ago.
 
@@ -3688,7 +3704,7 @@ int32_t populate_id_htable(uintptr_t unfiltered_ct, uintptr_t* exclude_arr, uint
 	  id_htable[hashval] = item_uidx;
 	  break;
 	} else if (!memcmp(sptr, &(item_ids[hash_result * max_id_len]), slen + 1)) {
-	  LOGPRINTFWW("Error: Duplicate ID '%s'.\n", sptr);
+	  LOGERRPRINTFWW("Error: Duplicate ID '%s'.\n", sptr);
 	  return RET_INVALID_FORMAT;
 	}
 	// defend against overflow
@@ -4346,19 +4362,19 @@ int32_t resolve_or_add_chrom_name(Chrom_info* chrom_info_ptr, char* bufptr, int3
   }
   if (*bufptr == '#') {
     // this breaks VCF and PLINK 2 binary
-    logprint("Error: Chromosome/contig names may not begin with '#'.\n");
+    logerrprint("Error: Chromosome/contig names may not begin with '#'.\n");
     return RET_INVALID_FORMAT;
   }
   if (slen > MAX_ID_LEN) {
     if (line_idx) {
-      LOGPRINTFWW("Error: Line %" PRIuPTR " of %s has an excessively long chromosome/contig name.  (The " PROG_NAME_CAPS " limit is " MAX_ID_LEN_STR " characters.)\n", line_idx, file_descrip);
+      LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s has an excessively long chromosome/contig name.  (The " PROG_NAME_CAPS " limit is " MAX_ID_LEN_STR " characters.)\n", line_idx, file_descrip);
     } else {
-      LOGPRINTFWW("Error: Excessively long chromosome/contig name in %s. (The " PROG_NAME_CAPS " limit is " MAX_ID_LEN_STR " characters.)\n", file_descrip);
+      LOGERRPRINTFWW("Error: Excessively long chromosome/contig name in %s. (The " PROG_NAME_CAPS " limit is " MAX_ID_LEN_STR " characters.)\n", file_descrip);
     }
     return RET_INVALID_FORMAT;
   }
   if (chrom_code_end == MAX_POSSIBLE_CHROM) {
-    logprint("Error: Too many distinct nonstandard chromosome/contig names.\n");
+    logerrprint("Error: Too many distinct nonstandard chromosome/contig names.\n");
     return RET_INVALID_FORMAT;
   }
   nonstd_names[chrom_code_end] = (char*)malloc(slen + 1);
@@ -4936,7 +4952,7 @@ int32_t sort_item_ids_noalloc(char* sorted_ids, uint32_t* id_map, uintptr_t unfi
       if (tptr) {
         *tptr = ' ';
       }
-      LOGPRINTFWW("Error: Duplicate ID '%s'.\n", dup_id);
+      LOGERRPRINTFWW("Error: Duplicate ID '%s'.\n", dup_id);
       return RET_INVALID_FORMAT;
     }
   }
@@ -7591,11 +7607,11 @@ int32_t string_range_list_to_bitfield(char* header_line, uint32_t item_ct, uint3
   string_range_list_to_bitfield_ret_INVALID_CMDLINE_3:
     sprintf(logbuf, "Error: Missing --%s token in %s.\n", range_list_flag, file_descrip);
   string_range_list_to_bitfield_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_CMDLINE;
     break;
   string_range_list_to_bitfield_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -7669,7 +7685,7 @@ int32_t string_range_list_to_bitfield2(char* sorted_ids, uint32_t* id_map, uintp
   string_range_list_to_bitfield2_ret_INVALID_CMDLINE_3:
     sprintf(logbuf, "Error: --%s ID not found.\n", range_list_flag);
   string_range_list_to_bitfield2_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_CMDLINE;
     break;
   }
@@ -7721,7 +7737,7 @@ int32_t conditional_allocate_non_autosomal_markers(Chrom_info* chrom_info_ptr, u
     LOGPRINTF("Excluding %u variant%s on non-autosomes from %s.\n", *newly_excluded_ct_ptr, (*newly_excluded_ct_ptr == 1)? "" : "s", calc_descrip);
   }
   if (*newly_excluded_ct_ptr == marker_ct) {
-    logprint("Error: No variants remaining.\n");
+    logerrprint("Error: No variants remaining.\n");
     return RET_INVALID_CMDLINE;
   }
   if (!(*newly_excluded_ct_ptr)) {
@@ -8788,7 +8804,7 @@ int32_t open_and_size_string_list(char* fname, FILE** infile_ptr, uintptr_t* lis
   while (fgets(tbuf, MAXLINELEN, *infile_ptr)) {
     line_idx++;
     if (!tbuf[MAXLINELEN - 1]) {
-      LOGPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, fname);
+      LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, fname);
       goto open_and_size_string_list_ret_INVALID_FORMAT;
     }
     bufptr = skip_initial_spaces(tbuf);
@@ -8858,7 +8874,7 @@ int32_t open_and_skip_first_lines(FILE** infile_ptr, char* fname, char* loadbuf,
   for (line_idx = 1; line_idx <= lines_to_skip; line_idx++) {
     if (!fgets(loadbuf, loadbuf_size, *infile_ptr)) {
       if (feof(*infile_ptr)) {
-	LOGPRINTFWW("Error: Fewer lines than expected in %s.\n", fname);
+	LOGERRPRINTFWW("Error: Fewer lines than expected in %s.\n", fname);
 	return RET_INVALID_FORMAT;
       } else {
 	return RET_READ_FAIL;
@@ -8866,7 +8882,7 @@ int32_t open_and_skip_first_lines(FILE** infile_ptr, char* fname, char* loadbuf,
     }
     if (!(loadbuf[loadbuf_size - 1])) {
       if ((loadbuf_size == MAXLINELEN) || (loadbuf_size == MAXLINEBUFLEN)) {
-	LOGPRINTFWW("Error: Line %u of %s is pathologically long.\n", line_idx, fname);
+	LOGERRPRINTFWW("Error: Line %u of %s is pathologically long.\n", line_idx, fname);
 	return RET_INVALID_FORMAT;
       } else {
         return RET_NOMEM;
@@ -8881,8 +8897,17 @@ int32_t load_to_first_token(FILE* infile, uintptr_t loadbuf_size, char comment_c
   while (fgets(loadbuf, loadbuf_size, infile)) {
     line_idx++;
     if (!(loadbuf[loadbuf_size - 1])) {
+      // PLINK 1.9 has two text line loading modes: "regular" and "long".
+      // * "Regular" mode limits lines to about MAXLINELEN (about 128k as of
+      //   this writing) characters.
+      // * "Long" mode theoretically accepts lines up to about MAXLINEBUFLEN
+      //   (~2 GB) characters but degrades gracefully if less memory is
+      //   available (in that case, an out-of-memory instead of an
+      //   invalid-format error is reported on fgets overflow).  Any long
+      //   buffer size larger than MAXLINELEN should work properly with
+      //   plink_common.
       if ((loadbuf_size == MAXLINELEN) || (loadbuf_size == MAXLINEBUFLEN)) {
-	LOGPRINTF("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, file_descrip);
+	LOGERRPRINTF("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, file_descrip);
 	return RET_INVALID_FORMAT;
       } else {
 	return RET_NOMEM;
@@ -8899,7 +8924,7 @@ int32_t load_to_first_token(FILE* infile, uintptr_t loadbuf_size, char comment_c
   if (!feof(infile)) {
     return RET_READ_FAIL;
   }
-  LOGPRINTF("Error: Empty %s.\n", file_descrip);
+  LOGERRPRINTF("Error: Empty %s.\n", file_descrip);
   return RET_INVALID_FORMAT;
 }
 
@@ -9005,7 +9030,7 @@ int32_t scan_max_strlen(char* fname, uint32_t colnum, uint32_t colnum2, uint32_t
     retval = RET_READ_FAIL;
     break;
   scan_max_strlen_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -9074,7 +9099,7 @@ int32_t scan_max_fam_indiv_strlen(char* fname, uint32_t colnum, uintptr_t* max_s
     retval = RET_READ_FAIL;
     break;
   scan_max_fam_indiv_strlen_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
diff --git a/plink_common.h b/plink_common.h
index 55d31e6..e1b6e94 100644
--- a/plink_common.h
+++ b/plink_common.h
@@ -404,6 +404,7 @@
 #define RECODE_INCLUDE_ALT 0x8000000
 #define RECODE_BGZ 0x10000000
 #define RECODE_GEN_GZ 0x20000000
+#define RECODE_OMIT_NONMALE_Y 0x40000000
 
 #define GENOME_OUTPUT_GZ 1
 #define GENOME_REL_CHECK 2
@@ -443,20 +444,21 @@
 #define MENDEL_FILTER_VAR_FIRST 2
 #define MENDEL_DUOS 4
 #define MENDEL_MULTIGEN 8
+#define MENDEL_SUMMARIES_ONLY 0x10
 
 #define DUMMY_MISSING_GENO 1
 #define DUMMY_MISSING_PHENO 2
 #define DUMMY_SCALAR_PHENO 4
 #define DUMMY_ACGT 8
-#define DUMMY_1234 16
-#define DUMMY_12 32
+#define DUMMY_1234 0x10
+#define DUMMY_12 0x20
 
 #define SIMULATE_QT 1
 #define SIMULATE_TAGS 2
 #define SIMULATE_HAPS 4
 #define SIMULATE_ACGT 8
-#define SIMULATE_1234 16
-#define SIMULATE_12 32
+#define SIMULATE_1234 0x10
+#define SIMULATE_12 0x20
 
 #define MODEL_ASSOC 1
 #define MODEL_FISHER 2
@@ -792,10 +794,16 @@ void logstr(const char* ss);
 
 void logprint(const char* ss);
 
+void logerrprint(const char* ss);
+
 void logprintb();
 
+void logerrprintb();
+
 #define LOGPRINTF(...) sprintf(logbuf, __VA_ARGS__); logprintb();
 
+#define LOGERRPRINTF(...) sprintf(logbuf, __VA_ARGS__); logerrprintb();
+
 // input for wordwrap/LOGPRINTFWW should have no intermediate '\n's.  If
 // suffix_len is 0, there should be a terminating \n.
 void wordwrap(char* ss, uint32_t suffix_len);
@@ -804,6 +812,8 @@ void wordwrap(char* ss, uint32_t suffix_len);
 
 #define LOGPRINTFWW(...) sprintf(logbuf, __VA_ARGS__); wordwrap(logbuf, 0); logprintb();
 
+#define LOGERRPRINTFWW(...) sprintf(logbuf, __VA_ARGS__); wordwrap(logbuf, 0); logerrprintb();
+
 // 5 = length of "done." suffix, which is commonly used
 #define LOGPRINTFWW5(...) sprintf(logbuf, __VA_ARGS__); wordwrap(logbuf, 5); logprintb();
 
diff --git a/plink_data.c b/plink_data.c
index 2082535..fe07a9c 100644
--- a/plink_data.c
+++ b/plink_data.c
@@ -48,7 +48,7 @@ int32_t sort_item_ids_nx(char** sorted_ids_ptr, uint32_t** id_map_ptr, uintptr_t
     if (tptr) {
       *tptr = ' ';
     }
-    LOGPRINTFWW("Error: Duplicate ID '%s'.\n", dup_id);
+    LOGERRPRINTFWW("Error: Duplicate ID '%s'.\n", dup_id);
     return RET_INVALID_FORMAT;
   }
   return 0;
@@ -188,22 +188,22 @@ uint32_t chrom_error(const char* extension, Chrom_info* chrom_info_ptr, char* ch
   chrom_str[slen] = '\0';
   logprint("\n");
   if (line_idx) {
-    LOGPRINTFWW("Error: Invalid chromosome code '%s' on line %" PRIuPTR " of %s.\n", chrom_str, line_idx, extension);
+    LOGERRPRINTFWW("Error: Invalid chromosome code '%s' on line %" PRIuPTR " of %s.\n", chrom_str, line_idx, extension);
   } else {
-    LOGPRINTFWW("Error: Invalid chromosome code '%s' in %s.\n", chrom_str, extension);
+    LOGERRPRINTFWW("Error: Invalid chromosome code '%s' in %s.\n", chrom_str, extension);
   }
   if ((raw_code > ((int32_t)chrom_info_ptr->max_code)) && ((raw_code <= MAX_CHROM_TEXTNUM + 4) || (raw_code >= MAX_POSSIBLE_CHROM))) {
     if (chrom_info_ptr->species != SPECIES_UNKNOWN) {
       if (chrom_info_ptr->species == SPECIES_HUMAN) {
-	logprint("(This is disallowed for humans.  Check if the problem is with your data, or if\nyou forgot to define a different chromosome set with e.g. --chr-set.).\n");
+	logerrprint("(This is disallowed for humans.  Check if the problem is with your data, or if\nyou forgot to define a different chromosome set with e.g. --chr-set.).\n");
       } else {
-	logprint("(This is disallowed by the PLINK 1.07 species flag you used.  You can\ntemporarily work around this restriction with --chr-set; contact the developers\nif you want the flag to be permanently redefined.)\n");
+	logerrprint("(This is disallowed by the PLINK 1.07 species flag you used.  You can\ntemporarily work around this restriction with --chr-set; contact the developers\nif you want the flag to be permanently redefined.)\n");
       }
     } else {
-      logprint("(This is disallowed by your --chr-set/--autosome-num parameters.  Check if the\nproblem is with your data, or your command line.)\n");
+      logerrprint("(This is disallowed by your --chr-set/--autosome-num parameters.  Check if the\nproblem is with your data, or your command line.)\n");
     }
   } else if (error_code == -2) {
-    logprint("(Use --allow-extra-chr to force it to be accepted.)\n");
+    logerrprint("(Use --allow-extra-chr to force it to be accepted.)\n");
   }
   return 1;
 }
@@ -268,7 +268,7 @@ int32_t load_map(FILE** mapfile_ptr, char* mapname, uint32_t* map_cols_ptr, uint
     goto load_map_ret_READ_FAIL;
   }
   if (!unfiltered_marker_ct) {
-    logprint("Error: No variants in .map file.\n");
+    logerrprint("Error: No variants in .map file.\n");
     goto load_map_ret_INVALID_FORMAT;
   }
   *unfiltered_marker_ct_ptr = unfiltered_marker_ct;
@@ -372,7 +372,7 @@ int32_t load_map(FILE** mapfile_ptr, char* mapname, uint32_t* map_cols_ptr, uint
   chrom_info_ptr->chrom_file_order_marker_idx[chroms_encountered_m1] = marker_uidx;
   *marker_exclude_ct_ptr = marker_exclude_ct;
   if (*marker_exclude_ct_ptr == unfiltered_marker_ct) {
-    logprint("Error: All variants excluded from .map file.\n");
+    logerrprint("Error: All variants excluded from .map file.\n");
     goto load_map_ret_ALL_MARKERS_EXCLUDED;
   }
   while (0) {
@@ -388,7 +388,7 @@ int32_t load_map(FILE** mapfile_ptr, char* mapname, uint32_t* map_cols_ptr, uint
   load_map_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of .map file has fewer tokens than expected.\n", line_idx);
   load_map_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_map_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -744,7 +744,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
       }
       if (ulii >= max_marker_id_len) {
 	if (ulii > MAX_ID_LEN) {
-          logprint("Error: Variant names are limited to " MAX_ID_LEN_STR " characters.\n");
+          logerrprint("Error: Variant names are limited to " MAX_ID_LEN_STR " characters.\n");
 	  goto load_bim_ret_INVALID_FORMAT;
 	}
 	max_marker_id_len = ulii + 1;
@@ -956,7 +956,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
     // down past that divided by 2.
     // PLINK/SEQ now supports a 64-bit count here, and few other tools do, so
     // it's appropriate to explicitly recommend it.
-    logprint("Error: PLINK does not support more than 2^31 - 3 variants.  We recommend other\nsoftware, such as PLINK/SEQ, for very deep studies of small numbers of genomes.\n");
+    logerrprint("Error: PLINK does not support more than 2^31 - 3 variants.  We recommend other\nsoftware, such as PLINK/SEQ, for very deep studies of small numbers of genomes.\n");
     goto load_bim_ret_INVALID_FORMAT;
   }
   if (from_slen || to_slen) {
@@ -1015,7 +1015,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
   }
 
   if (max_marker_id_len > MAX_ID_LEN_P1) {
-    logprint("Error: Variant names are limited to " MAX_ID_LEN_STR " characters.\n");
+    logerrprint("Error: Variant names are limited to " MAX_ID_LEN_STR " characters.\n");
     goto load_bim_ret_INVALID_FORMAT;
   }
   *unfiltered_marker_ct_ptr = unfiltered_marker_ct;
@@ -1062,7 +1062,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
     }
     if (max_marker_allele_len > 500000000) {
       // guard against overflows
-      logprint("Error: Alleles are limited to 500 million characters.\n");
+      logerrprint("Error: Alleles are limited to 500 million characters.\n");
       goto load_bim_ret_INVALID_FORMAT;
     }
     *max_marker_allele_len_ptr = max_marker_allele_len;
@@ -1264,7 +1264,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
 	  }
 	  bufptr4 = memcpyax(bufptr4, missing_template_seg[uii], missing_template_seg_len[uii], '\0');
 	  if (!strcmp(prev_new_id, bufptr5)) {
-	    LOGPRINTFWW("Error: Duplicate ID '%s' generated by --set-missing-var-ids.\n", prev_new_id);
+	    LOGERRPRINTFWW("Error: Duplicate ID '%s' generated by --set-missing-var-ids.\n", prev_new_id);
 	    goto load_bim_ret_INVALID_CMDLINE;
 	  }
 	  missing_ids_set++;
@@ -1282,7 +1282,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
     }
   }
   if (unfiltered_marker_ct == marker_exclude_ct) {
-    logprint("Error: All variants excluded.\n");
+    logerrprint("Error: All variants excluded.\n");
     goto load_bim_ret_ALL_MARKERS_EXCLUDED;
   }
   if (missing_mid_template && ((*map_is_unsorted_ptr) & UNSORTED_BP)) {
@@ -1318,15 +1318,15 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
     retval = RET_INVALID_CMDLINE;
     break;
   load_bim_ret_INVALID_BP_COORDINATE:
-    LOGPRINTF("Error: Invalid bp coordinate on line %" PRIuPTR " of %s.\n", line_idx, ftype_str);
+    LOGERRPRINTF("Error: Invalid bp coordinate on line %" PRIuPTR " of %s.\n", line_idx, ftype_str);
     retval = RET_INVALID_FORMAT;
     break;
   load_bim_ret_MISSING_TOKENS:
-    LOGPRINTF("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, ftype_str);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, ftype_str);
     retval = RET_INVALID_FORMAT;
     break;
   load_bim_ret_FROM_TO_DIFFERENT_CHROM:
-    logprint("Error: --from and --to variants are not on the same chromosome.\n");
+    logerrprint("Error: --from and --to variants are not on the same chromosome.\n");
     retval = RET_INVALID_FORMAT;
     break;
   load_bim_ret_DUPLICATE_ID:
@@ -1334,7 +1334,7 @@ int32_t load_bim(char* bimname, uint32_t* map_cols_ptr, uintptr_t* unfiltered_ma
     bufptr[uii] = '\0';
     LOGPREPRINTFWW("Error: Duplicate variant ID '%s' in %s.\n", bufptr, ftype_str);
   load_bim_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_bim_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -1496,7 +1496,7 @@ int32_t load_covars(char* covar_fname, uintptr_t unfiltered_sample_ct, uintptr_t
       }
     } else {
       if (header_absent) {
-	logprint("Error: --covar file doesn't have a header line for --covar-name.\n");
+	logerrprint("Error: --covar file doesn't have a header line for --covar-name.\n");
 	goto load_covars_ret_INVALID_FORMAT;
       }
       retval = string_range_list_to_bitfield(bufptr, covar_raw_ct, 0, covar_range_list_ptr, sorted_covar_name_flag_ids, covar_name_flag_id_map, covar_name_flag_seen_idxs, "covar-name", "--covar file header line", covars_active);
@@ -1548,7 +1548,7 @@ int32_t load_covars(char* covar_fname, uintptr_t unfiltered_sample_ct, uintptr_t
   //   (missing covariates are represented as the --missing-phenotype value).
   if (covar_range_list_ptr) {
     if (max_covar_name_len > MAX_ID_LEN_P1) {
-      logprint("Error: Covariate names are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: Covariate names are limited to " MAX_ID_LEN_STR " characters.\n");
       goto load_covars_ret_INVALID_FORMAT;
     }
     // not only --gxe
@@ -1642,7 +1642,7 @@ int32_t load_covars(char* covar_fname, uintptr_t unfiltered_sample_ct, uintptr_t
       continue;
     }
     if (is_set(already_seen, ii)) {
-      logprint("Error: Duplicate sample ID in --covar file.\n");
+      logerrprint("Error: Duplicate sample ID in --covar file.\n");
       goto load_covars_ret_INVALID_FORMAT;
     }
     set_bit(already_seen, ii);
@@ -1716,7 +1716,7 @@ int32_t load_covars(char* covar_fname, uintptr_t unfiltered_sample_ct, uintptr_t
     goto load_covars_ret_READ_FAIL;
   }
   if (loaded_sample_ct == missing_cov_ct) {
-    logprint("Error: No --covar values loaded.\n");
+    logerrprint("Error: No --covar values loaded.\n");
     goto load_covars_ret_INVALID_FORMAT;
   }
   if (covar_range_list_ptr) {
@@ -1754,7 +1754,7 @@ int32_t load_covars(char* covar_fname, uintptr_t unfiltered_sample_ct, uintptr_t
   load_covars_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Fewer tokens than expected on line %" PRIuPTR " of --covar file.\n", line_idx);
   load_covars_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_covars_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -2360,7 +2360,7 @@ int32_t zero_cluster_init(char* zerofname, uintptr_t unfiltered_marker_ct, uintp
   zero_cluster_init_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --zero-cluster file has fewer tokens than expected.\n", line_idx);
   zero_cluster_init_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -2682,7 +2682,7 @@ int32_t update_marker_chroms(Two_col_params* update_chr, uintptr_t unfiltered_ma
   update_marker_chroms_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --update-chr file has fewer tokens than expected.\n", line_idx);
   update_marker_chroms_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -3064,7 +3064,7 @@ int32_t flip_subset_init(char* flip_fname, char* flip_subset_fname, uintptr_t un
   }
   LOGPRINTF("--flip-subset: Flipping %u SNP%s for %u %s.\n", flip_marker_ct, (flip_marker_ct == 1)? "" : "s", flip_sample_ct, species_str(flip_sample_ct));
   if (miss_ct) {
-    LOGPRINTF("Warning: %" PRIuPTR " --flip-subset line%s skipped.\n", miss_ct, (miss_ct == 1)? "" : "s");
+    LOGERRPRINTF("Warning: %" PRIuPTR " --flip-subset line%s skipped.\n", miss_ct, (miss_ct == 1)? "" : "s");
   }
   while (0) {
   flip_subset_init_ret_NOMEM:
@@ -3077,7 +3077,7 @@ int32_t flip_subset_init(char* flip_fname, char* flip_subset_fname, uintptr_t un
     retval = RET_READ_FAIL;
     break;
   flip_subset_init_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -3398,9 +3398,9 @@ int32_t make_bed(FILE* bedfile, uintptr_t bed_offset, char* bimname, uint32_t ma
 	// main loop, but no real need to bother.
 	errptr = errflags[set_hh_missing? 0 : (set_me_missing? 1 : 2)];
 	if (map_is_unsorted) {
-	  LOGPRINTF("Error: --%s cannot be used on an unsorted .bim file.  Use\n--make-bed without --%s to sort by position first; then run\n--make-bed + --%s on the new fileset.\n", errptr, errptr, errptr);
+	  LOGERRPRINTF("Error: --%s cannot be used on an unsorted .bim file.  Use\n--make-bed without --%s to sort by position first; then run\n--make-bed + --%s on the new fileset.\n", errptr, errptr, errptr);
 	} else {
-	  LOGPRINTF("Error: --%s cannot be used with --merge-x/--split-x/--update-chr.\nFinish updating chromosome codes first.\n", errptr);
+	  LOGERRPRINTF("Error: --%s cannot be used with --merge-x/--split-x/--update-chr.\nFinish updating chromosome codes first.\n", errptr);
 	}
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto make_bed_ret_1;
@@ -3425,18 +3425,18 @@ int32_t make_bed(FILE* bedfile, uintptr_t bed_offset, char* bimname, uint32_t ma
 	  }
 	} else if (mergex || splitx_bound2) {
 	  if (splitx_bound2 && is_set(chrom_info_ptr->chrom_mask, chrom_info_ptr->xy_code)) {
-	    logprint("Error: --split-x cannot be used when the dataset already contains an XY region.\n");
+	    logerrprint("Error: --split-x cannot be used when the dataset already contains an XY region.\n");
 	    goto make_bed_ret_INVALID_CMDLINE;
 	  }
 	  if (merge_or_split_x(mergex, splitx_bound1, splitx_bound2, unfiltered_marker_ct, marker_exclude, marker_ct, marker_pos, chrom_info_ptr, ll_buf)) {
 	    if (!(misc_flags & MISC_SPLIT_MERGE_NOFAIL)) {
 	      if (mergex) {
-		logprint("Error: --merge-x requires XY pseudo-autosomal region data.  (Use 'no-fail' to\nforce --make-bed to proceed anyway.\n");
+		logerrprint("Error: --merge-x requires XY pseudo-autosomal region data.  (Use 'no-fail' to\nforce --make-bed to proceed anyway.\n");
 	      } else {
 		if (!is_set(chrom_info_ptr->chrom_mask, chrom_info_ptr->x_code)) {
-		  logprint("Error: --split-x requires X chromosome data.  (Use 'no-fail' to force\n--make-bed to proceed anyway.\n");
+		  logerrprint("Error: --split-x requires X chromosome data.  (Use 'no-fail' to force\n--make-bed to proceed anyway.\n");
 		} else {
-		  LOGPRINTFWW("Error: No X chromosome loci have bp positions <= %u or >= %u. (Use 'no-fail' to force --make-bed to proceed anyway.)\n", splitx_bound1, splitx_bound2);
+		  LOGERRPRINTFWW("Error: No X chromosome loci have bp positions <= %u or >= %u. (Use 'no-fail' to force --make-bed to proceed anyway.)\n", splitx_bound1, splitx_bound2);
 		}
 	      }
 	      goto make_bed_ret_INVALID_CMDLINE;
@@ -3812,13 +3812,13 @@ int32_t load_fam(char* famname, uint32_t fam_cols, uint32_t tmp_fam_col_6, int32
     goto load_fam_ret_READ_FAIL;
   }
   if (!unfiltered_sample_ct) {
-    logprint("Error: Nobody in .fam file.\n");
+    logerrprint("Error: Nobody in .fam file.\n");
     goto load_fam_ret_INVALID_FORMAT;
   }
   // don't yet need to enforce separate FID and IID limits, but in theory this
   // may change
   if ((max_sample_id_len > 2 * MAX_ID_LEN_P1) || (max_paternal_id_len > MAX_ID_LEN_P1) || (max_maternal_id_len > MAX_ID_LEN_P1)) {
-    logprint("Error: FIDs and IIDs are limited to " MAX_ID_LEN_STR " characters.\n");
+    logerrprint("Error: FIDs and IIDs are limited to " MAX_ID_LEN_STR " characters.\n");
     goto load_fam_ret_INVALID_FORMAT;
   }
   wkspace_reset(wkspace_mark);
@@ -3970,7 +3970,7 @@ int32_t load_fam(char* famname, uint32_t fam_cols, uint32_t tmp_fam_col_6, int32
   load_fam_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of .fam file has fewer tokens than expected.\n", line_idx);
   load_fam_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_fam_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -4057,7 +4057,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
   if (single_chr && (!allow_extra_chroms)) {
     ii = get_chrom_code_raw(single_chr);
     if (!is_set(chrom_info_ptr->chrom_mask, ii)) {
-      logprint("Error: --oxford-single-chr chromosome code is excluded by chromosome filter.\n");
+      logerrprint("Error: --oxford-single-chr chromosome code is excluded by chromosome filter.\n");
       goto oxford_to_bed_ret_INVALID_CMDLINE;
     }
   }
@@ -4129,7 +4129,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
       if (ferror(infile)) {
 	goto oxford_to_bed_ret_READ_FAIL;
       }
-      logprint("Error: Empty --data/--sample file.\n");
+      logerrprint("Error: Empty --data/--sample file.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (!tbuf[MAXLINELEN - 1]) {
@@ -4175,10 +4175,10 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
   }
   if (pheno_name) {
     if (!pheno_col) {
-      logprint("Error: --oxford-pheno-name parameter not found in .sample file header.\n");
+      logerrprint("Error: --oxford-pheno-name parameter not found in .sample file header.\n");
       goto oxford_to_bed_ret_INVALID_CMDLINE;
     } else if (sex_col > pheno_col) {
-      logprint("Error: .sample phenotype column(s) should be after sex covariate.\n");
+      logerrprint("Error: .sample phenotype column(s) should be after sex covariate.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
   }
@@ -4188,7 +4188,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
       if (ferror(infile)) {
 	goto oxford_to_bed_ret_READ_FAIL;
       }
-      logprint("Error: Only one nonempty line in .sample file.\n");
+      logerrprint("Error: Only one nonempty line in .sample file.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (!tbuf[MAXLINELEN - 1]) {
@@ -4215,7 +4215,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
   while (col_idx < col_ct) {
     bufptr = skip_initial_spaces(bufptr);
     if (is_eoln_kns(*bufptr)) {
-      logprint("Error: Second .sample header line has fewer tokens than the first.\n");
+      logerrprint("Error: Second .sample header line has fewer tokens than the first.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (bufptr[1] > ' ') {
@@ -4223,13 +4223,13 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
     }
     cc = *bufptr;
     if ((col_idx == sex_col) && (cc != 'D')) {
-      logprint("Error: .sample sex column is not of type 'D'.\n");
+      logerrprint("Error: .sample sex column is not of type 'D'.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (!pheno_col) {
       if ((cc == 'B') || (cc == 'P')) {
 	if (sex_col > col_idx) {
-          logprint("Error: .sample phenotype column(s) should be after sex covariate.\n");
+          logerrprint("Error: .sample phenotype column(s) should be after sex covariate.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
 	pheno_col = col_idx;
@@ -4239,7 +4239,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
     } else if (col_idx == pheno_col) {
       is_binary_pheno = (cc == 'B');
       if ((!is_binary_pheno) && (cc != 'P')) {
-        logprint("Error: --oxford-pheno-name parameter does not refer to a binary or continuous\nphenotype.\n");
+        logerrprint("Error: --oxford-pheno-name parameter does not refer to a binary or continuous\nphenotype.\n");
 	goto oxford_to_bed_ret_INVALID_CMDLINE;
       }
       break;
@@ -4250,7 +4250,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
   if (is_binary_pheno) {
     // check for pathological case
     if ((bsearch_str("0", 1, sorted_mc, max_mc_len, mc_ct) != -1) || (bsearch_str("1", 1, sorted_mc, max_mc_len, mc_ct) != -1)) {
-      logprint("Error: '0' and '1' are unacceptable missing case/control phenotype codes.\n");
+      logerrprint("Error: '0' and '1' are unacceptable missing case/control phenotype codes.\n");
       goto oxford_to_bed_ret_INVALID_CMDLINE;
     }
   }
@@ -4339,7 +4339,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
     sample_ct++;
   }
   if (!sample_ct) {
-    logprint("Error: No samples in .sample file.\n");
+    logerrprint("Error: No samples in .sample file.\n");
     goto oxford_to_bed_ret_INVALID_FORMAT;
   }
   if (fclose_null(&infile)) {
@@ -4390,7 +4390,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
       }
       if (!loadbuf[loadbuf_size - 1]) {
 	if (loadbuf_size == MAXLINEBUFLEN) {
-	  LOGPRINTF("Error: Line %" PRIuPTR " of .gen file is pathologically long.\n", line_idx);
+	  LOGERRPRINTF("Error: Line %" PRIuPTR " of .gen file is pathologically long.\n", line_idx);
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
 	goto oxford_to_bed_ret_NOMEM;
@@ -4608,7 +4608,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
       }
     }
     if (!marker_ct) {
-      logprint("Error: Empty .gen file.\n");
+      logerrprint("Error: Empty .gen file.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
   } else {
@@ -4632,20 +4632,20 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
       goto oxford_to_bed_ret_READ_FAIL;
     }
     if (uint_arr[1] > uint_arr[0]) {
-      logprint("Error: Invalid .bgen header.\n");
+      logerrprint("Error: Invalid .bgen header.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     raw_marker_ct = uint_arr[2];
     if (!raw_marker_ct) {
-      logprint("Error: .bgen file contains no markers.\n");
+      logerrprint("Error: .bgen file contains no markers.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (uint_arr[3] != sample_ct) {
-      logprint("Error: --bgen and --sample files contain different numbers of samples.\n");
+      logerrprint("Error: --bgen and --sample files contain different numbers of samples.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (uint_arr[4] && (uint_arr[4] != 0x6e656762)) {
-      logprint("Error: Invalid .bgen magic number.\n");
+      logerrprint("Error: Invalid .bgen magic number.\n");
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
     if (fseeko(infile, uint_arr[1], SEEK_SET)) {
@@ -4657,11 +4657,11 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
     if (uii & (~5)) {
       uii = (uii >> 2) & 15;
       if (uii == 2) {
-	logprint("Error: BGEN v1.2 input requires PLINK 2.0 (under development as of this\nwriting).  Use gen-convert to downcode to BGEN v1.1 if you want to process this\ndata with PLINK 1.9.\n");
+	logerrprint("Error: BGEN v1.2 input requires PLINK 2.0 (under development as of this\nwriting).  Use gen-convert to downcode to BGEN v1.1 if you want to process this\ndata with PLINK 1.9.\n");
       } else if (uii > 2) {
-	logprint("Error: Unrecognized BGEN version.  Use gen-convert or a similar tool to\ndowncode to BGEN v1.1 if you want to process this data with PLINK 1.9.\n");
+	logerrprint("Error: Unrecognized BGEN version.  Use gen-convert or a similar tool to\ndowncode to BGEN v1.1 if you want to process this data with PLINK 1.9.\n");
       } else {
-        logprint("Error: Unrecognized flags in .bgen header.  (PLINK 1.9 only supports\nBGEN v1.0 and v1.1.)\n");
+        logerrprint("Error: Unrecognized flags in .bgen header.  (PLINK 1.9 only supports\nBGEN v1.0 and v1.1.)\n");
       }
       goto oxford_to_bed_ret_INVALID_FORMAT;
     }
@@ -4671,7 +4671,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
     bgen_compressed = uii & 1;
     bgen_multichar_alleles = (uii >> 2) & 1;
     if ((!bgen_multichar_alleles) && (!snpid_chr) && (chrom_info_ptr->species != SPECIES_HUMAN)) {
-      logprint("Error: BGEN v1.0 files can only support nonhuman genomes if the SNP ID field is\nused for chromosome codes.\n");
+      logerrprint("Error: BGEN v1.0 files can only support nonhuman genomes if the SNP ID field is\nused for chromosome codes.\n");
       goto oxford_to_bed_ret_INVALID_CMDLINE;
     }
     if (!is_randomized) {
@@ -4683,7 +4683,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	goto oxford_to_bed_ret_READ_FAIL;
       }
       if (uii != sample_ct) {
-	logprint("Error: Unexpected number of samples specified in SNP block header.\n");
+	logerrprint("Error: Unexpected number of samples specified in SNP block header.\n");
 	goto oxford_to_bed_ret_INVALID_FORMAT;
       }
       if (bgen_multichar_alleles) {
@@ -4697,7 +4697,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	  bufptr = loadbuf;
 	} else {
 	  if (!usii) {
-	    logprint("Error: Length-0 SNP ID in .bgen file.\n");
+	    logerrprint("Error: Length-0 SNP ID in .bgen file.\n");
 	    goto oxford_to_bed_ret_INVALID_FORMAT;
 	  }
 	  if (fread(loadbuf, 1, usii, infile) < usii) {
@@ -4710,7 +4710,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	  goto oxford_to_bed_ret_READ_FAIL;
 	}
 	if (!usjj) {
-	  logprint("Error: Length-0 rsID in .bgen file.\n");
+	  logerrprint("Error: Length-0 rsID in .bgen file.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
 	if (fread(bufptr, 1, usjj, infile) < usjj) {
@@ -4722,7 +4722,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	}
 	if (!snpid_chr) {
 	  if (!uskk) {
-	    logprint("Error: Length-0 chromosome ID in .bgen file.\n");
+	    logerrprint("Error: Length-0 chromosome ID in .bgen file.\n");
 	    goto oxford_to_bed_ret_INVALID_FORMAT;
 	  }
 	  usii = uskk;
@@ -4746,7 +4746,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	  goto oxford_to_bed_ret_READ_FAIL;
 	}
 	if (!uint_arr[1]) {
-	  logprint("Error: Length-0 allele ID in .bgen file.\n");
+	  logerrprint("Error: Length-0 allele ID in .bgen file.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
         ii = get_chrom_code(chrom_info_ptr, bufptr2);
@@ -4795,7 +4795,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	  if (loadbuf_size < MAXLINEBUFLEN) {
 	    goto oxford_to_bed_ret_NOMEM;
 	  }
-	  logprint("Error: Excessively long allele in .bgen file.\n");
+	  logerrprint("Error: Excessively long allele in .bgen file.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
         if (fread(loadbuf, 1, uint_arr[1], infile) < uint_arr[1]) {
@@ -4809,7 +4809,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	  if (loadbuf_size < MAXLINEBUFLEN) {
 	    goto oxford_to_bed_ret_NOMEM;
 	  }
-	  logprint("Error: Excessively long allele in .bgen file.\n");
+	  logerrprint("Error: Excessively long allele in .bgen file.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
 	bufptr = &(loadbuf[uint_arr[1] + 1]);
@@ -4846,7 +4846,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	    } else if (ii > 252) {
 	      ii = ii - 228;
 	    } else {
-	      logprint("Error: Invalid chromosome code in BGEN v1.0 file.\n");
+	      logerrprint("Error: Invalid chromosome code in BGEN v1.0 file.\n");
 	      goto oxford_to_bed_ret_INVALID_FORMAT;
 	    }
 	  }
@@ -4914,7 +4914,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	  if (loadbuf_size < MAXLINEBUFLEN / 2) {
 	    goto oxford_to_bed_ret_NOMEM;
 	  }
-	  logprint("Error: Excessively long compressed SNP block in .bgen file.\n");
+	  logerrprint("Error: Excessively long compressed SNP block in .bgen file.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
 	if (fread(loadbuf, 1, uii, infile) < uii) {
@@ -4922,7 +4922,7 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
 	}
         zlib_ulongf = 6 * sample_ct;
 	if (uncompress((Bytef*)bgen_probs, &zlib_ulongf, (Bytef*)loadbuf, uii) != Z_OK) {
-	  logprint("Error: Invalid compressed SNP block in .bgen file.\n");
+	  logerrprint("Error: Invalid compressed SNP block in .bgen file.\n");
 	  goto oxford_to_bed_ret_INVALID_FORMAT;
 	}
       } else {
@@ -5055,31 +5055,31 @@ int32_t oxford_to_bed(char* genname, char* samplename, char* outname, char* outn
     retval = RET_WRITE_FAIL;
     break;
   oxford_to_bed_ret_INVALID_DOSAGE:
-    LOGPRINTF("Error: Line %" PRIuPTR " of .gen file has an invalid dosage value.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .gen file has an invalid dosage value.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   oxford_to_bed_ret_MISSING_TOKENS_GEN:
-    LOGPRINTF("Error: Line %" PRIuPTR " of .gen file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .gen file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   oxford_to_bed_ret_MISSING_TOKENS:
-    LOGPRINTF("Error: Line %" PRIuPTR " of .sample file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .sample file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   oxford_to_bed_ret_SAMPLE_LONG_LINE:
-    LOGPRINTF("Error: Line %" PRIuPTR " of .sample file is pathologically long.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .sample file is pathologically long.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   oxford_to_bed_ret_INVALID_SAMPLE_HEADER_2:
-    logprint("Error: Invalid second header line in .sample file.\n");
+    logerrprint("Error: Invalid second header line in .sample file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   oxford_to_bed_ret_INVALID_SAMPLE_HEADER_1:
-    logprint("Error: Invalid first header line in .sample file.\n");
+    logerrprint("Error: Invalid first header line in .sample file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   oxford_to_bed_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   oxford_to_bed_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -5337,7 +5337,7 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
       break;
     }
     if (!loadbuf[loadbuf_size - 1]) {
-      putchar('\n');
+      logprint("\n");
       if (loadbuf_size == MAXLINEBUFLEN) {
         sprintf(logbuf, "Error: Line %" PRIuPTR " of .ped file is pathologically long.\n", line_idx);
 	goto ped_to_bed_multichar_allele_ret_INVALID_FORMAT_2;
@@ -5431,7 +5431,7 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
     wkspace_base -= cur_slen_rdup;
     wkspace_left += cur_slen_rdup;
     if (!is_eoln_kns(*bufptr)) {
-      putchar('\n');
+      logprint("\n");
       sprintf(logbuf, "Error: Line %" PRIuPTR " of .ped file has more tokens than expected.\n", line_idx);
       goto ped_to_bed_multichar_allele_ret_INVALID_FORMAT_2;
     }
@@ -5493,7 +5493,7 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
       } else {
         sprintf(logbuf, "Warning: Variant %" PRIuPTR " %sallelic; setting rarest alleles missing.\n", marker_idx + 1, (uii? "quad" : "tri"));
       }
-      logprintb();
+      logerrprintb();
       get_top_two(&(marker_allele_cts[4 * marker_idx]), uii? 4 : 3, &ulii, &uljj);
       uii = map_is_unsorted? map_reverse[marker_idx] : marker_idx;
     } else {
@@ -5619,7 +5619,7 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
 	      ped_next_thresh = ftello(*pedfile_ptr);
 	    }
 	    if (!fgets(loadbuf, ped_buflen, *pedfile_ptr)) {
-	      goto ped_to_bed_multichar_allele_ret_READ_FAIL_2;
+	      goto ped_to_bed_multichar_allele_ret_READ_FAIL;
 	    }
 	    col1_ptr = skip_initial_spaces(loadbuf);
 	  } while (is_eoln_or_comment(*col1_ptr));
@@ -5627,10 +5627,10 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
 	} else {
 	  ped_next_thresh = line_starts[sample_idx];
 	  if (fseeko(*pedfile_ptr, line_starts[sample_idx], SEEK_SET)) {
-	    goto ped_to_bed_multichar_allele_ret_READ_FAIL_2;
+	    goto ped_to_bed_multichar_allele_ret_READ_FAIL;
 	  }
 	  if (!fgets(loadbuf, ped_buflen, *pedfile_ptr)) {
-	    goto ped_to_bed_multichar_allele_ret_READ_FAIL_2;
+	    goto ped_to_bed_multichar_allele_ret_READ_FAIL;
 	  }
 	  bufptr = loadbuf;
 	}
@@ -5722,7 +5722,7 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
       fflush(stdout);
     }
     if (fwrite_checked(writebuf, ujj * sample_ct4, *outfile_ptr)) {
-      goto ped_to_bed_multichar_allele_ret_WRITE_FAIL_2;
+      goto ped_to_bed_multichar_allele_ret_WRITE_FAIL;
     }
     if (!last_pass) {
       printf("\rPass %u:    \b\b\b", uii + 2);
@@ -5742,36 +5742,32 @@ int32_t ped_to_bed_multichar_allele(FILE** pedfile_ptr, FILE** outfile_ptr, char
   ped_to_bed_multichar_allele_ret_OPEN_FAIL:
     retval = RET_OPEN_FAIL;
     break;
-  ped_to_bed_multichar_allele_ret_READ_FAIL_2:
-    putchar('\n');
   ped_to_bed_multichar_allele_ret_READ_FAIL:
     retval = RET_READ_FAIL;
     break;
-  ped_to_bed_multichar_allele_ret_WRITE_FAIL_2:
-    putchar('\n');
   ped_to_bed_multichar_allele_ret_WRITE_FAIL:
     retval = RET_WRITE_FAIL;
     break;
   ped_to_bed_multichar_allele_ret_INVALID_FORMAT_6:
     wkspace_base -= cur_slen_rdup;
     wkspace_left += cur_slen_rdup;
-    putchar('\n');
+    logprint("\n");
     if (retval != RET_NOMEM) {
-      LOGPRINTF("Error: More than 4 different alleles at variant %u%s.\n", uii + 1, map_is_unsorted? " (post-sort/filter)" : "");
+      LOGERRPRINTF("Error: More than 4 different alleles at variant %u%s.\n", uii + 1, map_is_unsorted? " (post-sort/filter)" : "");
     }
     break;
   ped_to_bed_multichar_allele_ret_INVALID_FORMAT_4:
     wkspace_base -= cur_slen_rdup;
     wkspace_left += cur_slen_rdup;
-    putchar('\n');
-    LOGPRINTF("Error: Half-missing call in .ped file at variant %" PRIuPTR ", line %" PRIuPTR ".\n", marker_uidx + 1, line_idx);
+    logprint("\n");
+    LOGERRPRINTF("Error: Half-missing call in .ped file at variant %" PRIuPTR ", line %" PRIuPTR ".\n", marker_uidx + 1, line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   ped_to_bed_multichar_allele_ret_MISSING_TOKENS:
-    putchar('\n');
+    logprint("\n");
     sprintf(logbuf, "Error: Line %" PRIuPTR " of .ped file has fewer tokens than expected.\n", line_idx);
   ped_to_bed_multichar_allele_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -5852,9 +5848,9 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
   if (!mapfile) {
     uii = strlen(mapname);
     if ((uii > 8) && ((!memcmp(&(mapname[uii - 8]), ".ped.map", 8)) || (!memcmp(&(mapname[uii - 8]), ".map.map", 8)))) {
-      LOGPRINTFWW("Error: Failed to open %s. (--file expects a filename *prefix*; '.ped' and '.map' are automatically appended.)\n", mapname);
+      LOGERRPRINTFWW("Error: Failed to open %s. (--file expects a filename *prefix*; '.ped' and '.map' are automatically appended.)\n", mapname);
     } else {
-      LOGPRINTFWW(errstr_fopen, mapname);
+      LOGERRPRINTFWW(errstr_fopen, mapname);
     }
     goto ped_to_bed_ret_OPEN_FAIL;
   }
@@ -5863,7 +5859,7 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
     if (line_idx) {
       goto ped_to_bed_ret_MISSING_TOKENS_MAP;
     } else {
-      logprint("Error: Empty .map file.\n");
+      logerrprint("Error: Empty .map file.\n");
       goto ped_to_bed_ret_INVALID_FORMAT;
     }
   }
@@ -5989,7 +5985,8 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
     line_idx++;
     if (!loadbuf[loadbuf_size - 1]) {
       if (loadbuf_size == MAXLINEBUFLEN) {
-	sprintf(logbuf, "\nError: Line %" PRIuPTR " of .ped file is pathologically long.\n", line_idx);
+	logprint("\n");
+	sprintf(logbuf, "Error: Line %" PRIuPTR " of .ped file is pathologically long.\n", line_idx);
 	goto ped_to_bed_ret_INVALID_FORMAT_2;
       } else {
         goto ped_to_bed_ret_NOMEM;
@@ -6013,7 +6010,8 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
       goto ped_to_bed_ret_MISSING_TOKENS_PED;
     }
     if ((bufptr - col1_ptr) > (MAXLINELEN / 2) - 4) {
-      sprintf(logbuf, "\nError: Line %" PRIuPTR " of .ped file has a pathologically long token.\n", line_idx);
+      logprint("\n");
+      sprintf(logbuf, "Error: Line %" PRIuPTR " of .ped file has a pathologically long token.\n", line_idx);
       goto ped_to_bed_ret_INVALID_FORMAT_2;
     }
     if (fwrite_checked(col1_ptr, strlen_se(col1_ptr), outfile)) {
@@ -6103,7 +6101,8 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
       goto ped_to_bed_ret_READ_FAIL;
     }
     if (!sample_ct) {
-      sprintf(logbuf, "\nError: No %s in .ped file.\n", g_species_plural);
+      logprint("\n");
+      sprintf(logbuf, "Error: No %s in .ped file.\n", g_species_plural);
       goto ped_to_bed_ret_INVALID_FORMAT_2;
     }
     if (fclose_null(&outfile)) {
@@ -6142,7 +6141,7 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
 	} else {
 	  sprintf(logbuf, "Warning: Variant %" PRIuPTR " %sallelic; setting rarest alleles missing.\n", marker_idx + 1, (cc? "quad" : "tri"));
 	}
-	logprintb();
+	logerrprintb();
 	ujj = (cc? 4 : 3);
 	// insertion sort
 	for (uii = 1; uii < ujj; uii++) {
@@ -6269,7 +6268,7 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
 		ped_next_thresh = ftello(pedfile);
 	      }
 	      if (!fgets(loadbuf, ped_buflen, pedfile)) {
-		goto ped_to_bed_ret_READ_FAIL_2;
+		goto ped_to_bed_ret_READ_FAIL;
 	      }
 	      col1_ptr = skip_initial_spaces(loadbuf);
 	    } while (is_eoln_or_comment(*col1_ptr));
@@ -6277,10 +6276,10 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
 	  } else {
 	    ped_next_thresh = line_starts[sample_idx];
 	    if (fseeko(pedfile, line_starts[sample_idx], SEEK_SET)) {
-	      goto ped_to_bed_ret_READ_FAIL_2;
+	      goto ped_to_bed_ret_READ_FAIL;
 	    }
 	    if (!fgets(loadbuf, ped_buflen, pedfile)) {
-	      goto ped_to_bed_ret_READ_FAIL_2;
+	      goto ped_to_bed_ret_READ_FAIL;
 	    }
 	    bufptr = loadbuf;
 	  }
@@ -6359,7 +6358,7 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
 	fflush(stdout);
       }
       if (fwrite_checked(writebuf, ujj * sample_ct4, outfile)) {
-	goto ped_to_bed_ret_WRITE_FAIL_2;
+	goto ped_to_bed_ret_WRITE_FAIL;
       }
       if (!last_pass) {
 	printf("\rPass %u:    \b\b\b", uii + 2);
@@ -6379,7 +6378,7 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
   }
 
   if (fclose_null(&outfile)) {
-    goto ped_to_bed_ret_WRITE_FAIL_2;
+    goto ped_to_bed_ret_WRITE_FAIL;
   }
   putchar('\r');
   *outname_end = '\0';
@@ -6392,24 +6391,22 @@ int32_t ped_to_bed(char* pedname, char* mapname, char* outname, char* outname_en
   ped_to_bed_ret_OPEN_FAIL:
     retval = RET_OPEN_FAIL;
     break;
-  ped_to_bed_ret_READ_FAIL_2:
-    putchar('\n');
   ped_to_bed_ret_READ_FAIL:
     retval = RET_READ_FAIL;
     break;
-  ped_to_bed_ret_WRITE_FAIL_2:
-    putchar('\n');
   ped_to_bed_ret_WRITE_FAIL:
     retval = RET_WRITE_FAIL;
     break;
   ped_to_bed_ret_MISSING_TOKENS_MAP:
-    LOGPRINTF("\nError: Line %" PRIuPTR " of .map file has fewer tokens than expected.\n", line_idx);
+    logprint("\n");
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .map file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   ped_to_bed_ret_MISSING_TOKENS_PED:
-    sprintf(logbuf, "\nError: Line %" PRIuPTR " of .ped file has fewer tokens than expected.\n", line_idx);
+    logprint("\n");
+    sprintf(logbuf, "Error: Line %" PRIuPTR " of .ped file has fewer tokens than expected.\n", line_idx);
   ped_to_bed_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   ped_to_bed_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -6495,7 +6492,7 @@ int32_t lgen_to_bed(char* lgen_namebuf, char* outname, char* outname_end, int32_
   int32_t retval;
   int32_t ii;
   if (lgen_modifier == LGEN_ALLELE_COUNT) {
-    logprint("Error: --allele-count must be used with --reference.\n");
+    logerrprint("Error: --allele-count must be used with --reference.\n");
     goto lgen_to_bed_ret_INVALID_CMDLINE;
   }
 
@@ -6550,7 +6547,7 @@ int32_t lgen_to_bed(char* lgen_namebuf, char* outname, char* outname_end, int32_
   memset(marker_allele_ptrs, 0, 2 * marker_ct * sizeof(char*));
   sample_ct4 = (sample_ct + 3) / 4;
   if (wkspace_alloc_uc_checked(&writebuf, ((uintptr_t)marker_ct) * sample_ct4)) {
-    logprint("Error: Multipass .lgen -> .bed autoconversions are not yet supported.  Try\nusing --chr and/or --memory (perhaps with a better machine).\n");
+    logerrprint("Error: Multipass .lgen -> .bed autoconversions are not yet supported.  Try\nusing --chr and/or --memory (perhaps with a better machine).\n");
     goto lgen_to_bed_ret_CALC_NOT_YET_SUPPORTED;
   }
   if (sample_ct % 4) {
@@ -6953,7 +6950,7 @@ int32_t lgen_to_bed(char* lgen_namebuf, char* outname, char* outname_end, int32_
   if (!realpath(lgen_namebuf, tbuf))
 #endif
   {
-    LOGPRINTFWW("Error: Failed to open %s.\n", outname);
+    LOGERRPRINTFWW("Error: Failed to open %s.\n", outname);
     goto lgen_to_bed_ret_OPEN_FAIL;
   }
 #ifdef _WIN32
@@ -7007,25 +7004,25 @@ int32_t lgen_to_bed(char* lgen_namebuf, char* outname, char* outname_end, int32_
     retval = RET_WRITE_FAIL;
     break;
   lgen_to_bed_ret_LONG_LINE:
-    LOGPRINTF("Error: Line %" PRIuPTR " of .lgen file is pathologically long.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .lgen file is pathologically long.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   lgen_to_bed_ret_HALF_MISSING:
-    LOGPRINTF("Error: Half-missing genotype on line %" PRIuPTR " of .lgen file.\n", line_idx);
+    LOGERRPRINTF("Error: Half-missing genotype on line %" PRIuPTR " of .lgen file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   lgen_to_bed_ret_MISSING_IID:
-    LOGPRINTF("Error: Sample ID on line %" PRIuPTR " of .lgen file is missing from .fam file.\n", line_idx);
+    LOGERRPRINTF("Error: Sample ID on line %" PRIuPTR " of .lgen file is missing from .fam file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   lgen_to_bed_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of .lgen file has fewer tokens than expected.\n", line_idx);
   lgen_to_bed_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   lgen_to_bed_ret_NOT_BIALLELIC:
-    LOGPRINTFWW("Error: Variant '%s' in .lgen file has 3+ different alleles.\n", id_buf);
+    LOGERRPRINTFWW("Error: Variant '%s' in .lgen file has 3+ different alleles.\n", id_buf);
     retval = RET_INVALID_FORMAT;
     break;
   lgen_to_bed_ret_INVALID_CMDLINE:
@@ -7577,7 +7574,7 @@ int32_t transposed_to_bed(char* tpedname, char* tfamname, char* outname, char* o
       if (!is_space_or_eoln(*cptr2)) {
 	no_extra_cols = 0;
 	putchar('\r');
-	logprint("Warning: Extra columns in .tped file.  Ignoring.\n");
+	logerrprint("Warning: Extra columns in .tped file.  Ignoring.\n");
 	transposed_to_bed_print_pct(pct);
 	goto transposed_to_bed_nextline;
       }
@@ -7769,7 +7766,7 @@ int32_t transposed_to_bed(char* tpedname, char* tfamname, char* outname, char* o
     break;
   transposed_to_bed_ret_MISSING_TOKENS:
     putchar('\r');
-    LOGPRINTF("Error: Line %" PRIuPTR " of .tped file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .tped file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   transposed_to_bed_ret_INVALID_FORMAT:
@@ -7779,12 +7776,12 @@ int32_t transposed_to_bed(char* tpedname, char* tfamname, char* outname, char* o
     sprintf(logbuf, "Error: Line %" PRIuPTR " of .tped file has a half-missing call.\n", line_idx);
   transposed_to_bed_ret_INVALID_FORMAT_2R:
     putchar('\r');
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   transposed_to_bed_ret_TOO_MANY_ALLELES:
     putchar('\r');
-    LOGPRINTF("Error: More than four alleles at variant %" PRIuPTR ".\n", marker_ct - 1);
+    LOGERRPRINTF("Error: More than four alleles at variant %" PRIuPTR ".\n", marker_ct - 1);
     // retval already set
     break;
   }
@@ -7840,7 +7837,7 @@ int32_t vcf_sample_line(char* outname, char* outname_end, int32_t missing_pheno,
     bufptr2 = strchr(bufptr, ' ');
     if (bufptr2) {
       if (!vcf_idspace_to) {
-	logprint("Error: VCF/BCF2 sample ID contains space(s).  Use --vcf-idspace-to to convert\nthem to another character, or \"--id-delim ' '\" to interpret the spaces as\nFID/IID delimiters.\n");
+	logerrprint("Error: VCF/BCF2 sample ID contains space(s).  Use --vcf-idspace-to to convert\nthem to another character, or \"--id-delim ' '\" to interpret the spaces as\nFID/IID delimiters.\n");
 	goto vcf_sample_line_ret_INVALID_FORMAT;
       }
       do {
@@ -7863,7 +7860,7 @@ int32_t vcf_sample_line(char* outname, char* outname_end, int32_t missing_pheno,
       goto vcf_sample_line_ret_INVALID_FORMAT_2;
     }
     if ((*bufptr == '0') && (slen == 1)) {
-      logprint("Error: Sample ID cannot be '0'.\n");
+      logerrprint("Error: Sample ID cannot be '0'.\n");
       goto vcf_sample_line_ret_INVALID_FORMAT;
     }
     if (id_delim) {
@@ -7886,9 +7883,9 @@ int32_t vcf_sample_line(char* outname, char* outname_end, int32_t missing_pheno,
 	}
       }
       if (memchr(&(bufptr3[1]), (unsigned char)id_delim, (uintptr_t)(bufptr2 - &(bufptr3[1])))) {
-        LOGPRINTF("Error: Multiple instances of '%c' in sample ID.\n", id_delim);
+        LOGERRPRINTF("Error: Multiple instances of '%c' in sample ID.\n", id_delim);
 	if (id_delim == '_') {
-	  logprint("If you do not want '_' to be treated as a FID/IID delimiter, use --double-id or\n--const-fid to choose a different method of converting VCF sample IDs to PLINK\nIDs, or --id-delim to change the FID/IID delimiter.\n");
+	  logerrprint("If you do not want '_' to be treated as a FID/IID delimiter, use --double-id or\n--const-fid to choose a different method of converting VCF sample IDs to PLINK\nIDs, or --id-delim to change the FID/IID delimiter.\n");
 	}
         goto vcf_sample_line_ret_INVALID_FORMAT;
       }
@@ -7934,7 +7931,7 @@ int32_t vcf_sample_line(char* outname, char* outname_end, int32_t missing_pheno,
     retval = RET_WRITE_FAIL;
     break;
   vcf_sample_line_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   vcf_sample_line_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -8110,7 +8107,7 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
       continue;
     }
     if (*bufptr != '#') {
-      logprint("Error: Missing header line in .vcf file.\n");
+      logerrprint("Error: Missing header line in .vcf file.\n");
       goto vcf_to_bed_ret_INVALID_FORMAT;
     }
     if (bufptr[1] != '#') {
@@ -8118,12 +8115,12 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
     }
   }
   if (memcmp(bufptr, "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO", 38)) {
-    logprint("Error: Improperly formatted .vcf header line.\n");
+    logerrprint("Error: Improperly formatted .vcf header line.\n");
     goto vcf_to_bed_ret_INVALID_FORMAT;
   }
   bufptr = &(bufptr[38]);
   if (memcmp(bufptr, "\tFORMAT\t", 8) || (((unsigned char)bufptr[8]) <= ' ')) {
-    logprint("Error: No genotype data in .vcf file.\n");
+    logerrprint("Error: No genotype data in .vcf file.\n");
     goto vcf_to_bed_ret_INVALID_FORMAT;
   }
   retval = vcf_sample_line(outname, outname_end, missing_pheno, &(bufptr[8]), const_fid, double_id, id_delim, vcf_idspace_to, 'v', &sample_ct);
@@ -8189,7 +8186,7 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
       }
       retval = resolve_or_add_chrom_name(chrom_info_ptr, bufptr, &ii, line_idx, ".vcf file");
       if (retval) {
-	putchar('\n');
+	logprint("\n");
         goto vcf_to_bed_ret_1;
       }
     }
@@ -8205,8 +8202,8 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
       goto vcf_to_bed_ret_MISSING_TOKENS;
     }
     if ((((unsigned char)(*pos_str)) - '0') >= 10) {
-      sprintf(logbuf, "\nError: Invalid variant bp coordinate on line %" PRIuPTR " of .vcf file.\n", line_idx);
-      goto vcf_to_bed_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Invalid variant bp coordinate on line %" PRIuPTR " of .vcf file.\n", line_idx);
+      goto vcf_to_bed_ret_INVALID_FORMAT_2N;
     }
     ref_allele_ptr = strchr(++marker_id, '\t');
     if (!ref_allele_ptr) {
@@ -8224,9 +8221,9 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
     cc = *bufptr;
     // ',' < '.'
     while (1) {
-      if ((unsigned char)cc <= ',') {
-	sprintf(logbuf, "\nError: Invalid alternate allele on line %" PRIuPTR  " of .vcf file.\n", line_idx);
-	goto vcf_to_bed_ret_INVALID_FORMAT_2;
+      if ((unsigned char)cc <= ',' && (unsigned char)cc != '*') {
+	sprintf(logbuf, "Error: Invalid alternate allele on line %" PRIuPTR  " of .vcf file.\n", line_idx);
+	goto vcf_to_bed_ret_INVALID_FORMAT_2N;
       }
       bufptr2 = bufptr;
       do {
@@ -8235,8 +8232,8 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
       } while (((unsigned char)cc > ',') || (cc == '*'));
       if (((uintptr_t)(bufptr - bufptr2) == ref_allele_len) && (!memcmp(ref_allele_ptr, bufptr2, ref_allele_len))) {
 	if ((alt_ct != 1) || (cc == ',')) {
-	  sprintf(logbuf, "\nError: ALT allele duplicates REF allele on line %" PRIuPTR " of .vcf file.\n", line_idx);
-	  goto vcf_to_bed_ret_INVALID_FORMAT_2;
+	  sprintf(logbuf, "Error: ALT allele duplicates REF allele on line %" PRIuPTR " of .vcf file.\n", line_idx);
+	  goto vcf_to_bed_ret_INVALID_FORMAT_2N;
 	}
         *alt_alleles = '.'; // tolerate SHAPEIT output
       }
@@ -8247,8 +8244,8 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
       alt_ct++;      
     }
     if (cc != '\t') {
-      sprintf(logbuf, "\nError: Malformed ALT field on line %" PRIuPTR " of .vcf file.\n", line_idx);
-      goto vcf_to_bed_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Malformed ALT field on line %" PRIuPTR " of .vcf file.\n", line_idx);
+      goto vcf_to_bed_ret_INVALID_FORMAT_2N;
     }
     if (biallelic_strict && (alt_ct > 1)) {
       goto vcf_to_bed_skip3;
@@ -8264,8 +8261,8 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
 	continue;
       }
       if (scan_double(bufptr, &dxx)) {
-        sprintf(logbuf, "\nError: Invalid QUAL value on line %" PRIuPTR " of .vcf file.\n", line_idx);
-	goto vcf_to_bed_ret_INVALID_FORMAT_2;
+        sprintf(logbuf, "Error: Invalid QUAL value on line %" PRIuPTR " of .vcf file.\n", line_idx);
+	goto vcf_to_bed_ret_INVALID_FORMAT_2N;
       }
       if (dxx < vcf_min_qual) {
         marker_skip_ct++;
@@ -8791,37 +8788,39 @@ int32_t vcf_to_bed(char* vcfname, char* outname, char* outname_end, int32_t miss
     retval = RET_OPEN_FAIL;
     break;
   vcf_to_bed_ret_READ_FAIL:
-    putchar('\n');
     retval = RET_READ_FAIL;
     break;
   vcf_to_bed_ret_WRITE_FAIL:
-    putchar('\n');
     retval = RET_WRITE_FAIL;
     break;
   vcf_to_bed_ret_HALF_CALL_ERROR:
-    LOGPRINTF("\nError: Line %" PRIuPTR " of .vcf file has a GT half-call.\n", line_idx);
+    logprint("\n");
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .vcf file has a GT half-call.\n", line_idx);
     if (!vcf_half_call_explicit_error) {
-      logprint("Use --vcf-half-call to specify how these should be processed.\n");
+      logerrprint("Use --vcf-half-call to specify how these should be processed.\n");
     }
     retval = RET_INVALID_FORMAT;
     break;
   vcf_to_bed_ret_INVALID_GP:
     logprint("\n");
-    LOGPRINTF("Error: Line %" PRIuPTR " of .vcf file has an improperly formatted GP field.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .vcf file has an improperly formatted GP field.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   vcf_to_bed_ret_INVALID_GT:
-    LOGPRINTF("\nError: Line %" PRIuPTR " of .vcf file has an invalid GT field.\n", line_idx);
+    logprint("\n");
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .vcf file has an invalid GT field.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   vcf_to_bed_ret_MISSING_TOKENS:
-    LOGPRINTF("\nError: Line %" PRIuPTR " of .vcf file has fewer tokens than expected.\n", line_idx);
+    logprint("\n");
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of .vcf file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   vcf_to_bed_ret_LONG_LINE:
-    sprintf(logbuf, "\nError: Line %" PRIuPTR " of .vcf file is pathologically long.\n", line_idx);
-  vcf_to_bed_ret_INVALID_FORMAT_2:
-    logprintb();
+    sprintf(logbuf, "Error: Line %" PRIuPTR " of .vcf file is pathologically long.\n", line_idx);
+  vcf_to_bed_ret_INVALID_FORMAT_2N:
+    logprint("\n");
+    logerrprintb();
   vcf_to_bed_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -8873,7 +8872,7 @@ int32_t read_bcf_typed_integer(gzFile gz_infile, uint32_t* int_ptr) {
       break;
     }
   read_bcf_typed_integer_ret_INVALID_FORMAT_GENERIC:
-    logprint("Error: Improperly formatted .bcf file.\n");
+    logerrprint("Error: Improperly formatted .bcf file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -8894,7 +8893,7 @@ int32_t read_bcf_typed_string(gzFile gz_infile, char* readbuf, uint32_t maxlen,
       goto read_bcf_typed_string_ret_1;
     }
     if (slen > maxlen) {
-      logprint("Error: Excessively long typed string in .bcf file.\n");
+      logerrprint("Error: Excessively long typed string in .bcf file.\n");
       goto read_bcf_typed_string_ret_INVALID_FORMAT;
     }
   } else if ((((uint32_t)ii) & 0x0f) == 0x07) {
@@ -8917,7 +8916,7 @@ int32_t read_bcf_typed_string(gzFile gz_infile, char* readbuf, uint32_t maxlen,
       break;
     }
   read_bcf_typed_string_ret_INVALID_FORMAT_GENERIC:
-    logprint("Error: Improperly formatted .bcf file.\n");
+    logerrprint("Error: Improperly formatted .bcf file.\n");
   read_bcf_typed_string_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -9086,11 +9085,11 @@ int32_t bcf_to_bed(char* bcfname, char* outname, char* outname_end, int32_t miss
       if (!memcmp(&(linebuf[3]), "ORMAT=<ID=", 10)) {
 	if (!memcmp(&(linebuf[13]), "GT,", 3)) {
 	  if (gt_idx) {
-	    logprint("Error: Duplicate GT format specifier in .bcf file.\n");
+	    logerrprint("Error: Duplicate GT format specifier in .bcf file.\n");
 	    goto bcf_to_bed_ret_INVALID_FORMAT;
 	  }
 	  if (memcmp(&(linebuf[16]), "Number=1,Type=String,Description=", 33)) {
-	    logprint("Error: Unrecognized GT field format in .bcf file.\n");
+	    logerrprint("Error: Unrecognized GT field format in .bcf file.\n");
 	    goto bcf_to_bed_ret_INVALID_FORMAT;
 	  }
 	  gt_idx = stringdict_ct;
@@ -9142,11 +9141,11 @@ int32_t bcf_to_bed(char* bcfname, char* outname, char* outname_end, int32_t miss
     }
   }
   if (!gt_idx) {
-    logprint("Error: No GT field in .bcf header.\n");
+    logerrprint("Error: No GT field in .bcf header.\n");
     goto bcf_to_bed_ret_INVALID_FORMAT;
   }
   if (!contig_ct) {
-    logprint("Error: No contig fields in .bcf header.\n");
+    logerrprint("Error: No contig fields in .bcf header.\n");
     goto bcf_to_bed_ret_INVALID_FORMAT;
   }
   if (memcmp(linebuf, "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t", 46)) {
@@ -9388,7 +9387,7 @@ int32_t bcf_to_bed(char* bcfname, char* outname, char* outname_end, int32_t miss
 	if ((ukk == 3) || (ukk == 5)) {
 	  umm = 4; // int32, float = 4 bytes
 	} else if (ukk && (ukk > 2)) {
-	  logprint("Error: Unrecognized type in .bcf file.\n");
+	  logerrprint("Error: Unrecognized type in .bcf file.\n");
 	  goto bcf_to_bed_ret_INVALID_FORMAT;
 	} else {
 	  umm = ukk;
@@ -9418,13 +9417,13 @@ int32_t bcf_to_bed(char* bcfname, char* outname, char* outname_end, int32_t miss
       goto bcf_to_bed_marker_skip;
     }
     if (ukk == 5) {
-      logprint("Error: GT field cannot contain floating point values.\n");
+      logerrprint("Error: GT field cannot contain floating point values.\n");
       goto bcf_to_bed_ret_INVALID_FORMAT;
     }
     if (ujj * umm > 12) {
       // 12 = 12-ploid, or 6-ploid and >= 127 alleles, or triploid and >= 32767
       // alleles.  this is pretty darn generous.
-      logprint("Error: --bcf does not support GT vectors requiring >12 bytes per sample.\n");
+      logerrprint("Error: --bcf does not support GT vectors requiring >12 bytes per sample.\n");
       goto bcf_to_bed_ret_INVALID_FORMAT;
     }
     if ((uint32_t)((uint64_t)gzread(gz_infile, loadbuf, ujj * umm * sample_ct)) < ujj * umm * sample_ct) {
@@ -9666,7 +9665,7 @@ int32_t bcf_to_bed(char* bcfname, char* outname, char* outname_end, int32_t miss
     marker_skip_ct++;
   }
   if (!marker_ct) {
-    logprint("Error: No variants in .bcf file.\n");
+    logerrprint("Error: No variants in .bcf file.\n");
     goto bcf_to_bed_ret_INVALID_FORMAT;
   }
   if (gzclose(gz_infile) != Z_OK) {
@@ -9706,7 +9705,7 @@ int32_t bcf_to_bed(char* bcfname, char* outname, char* outname_end, int32_t miss
     retval = RET_WRITE_FAIL;
     break;
   bcf_to_bed_ret_INVALID_FORMAT_GENERIC:
-    logprint("Error: Improperly formatted .bcf file.\n");
+    logerrprint("Error: Improperly formatted .bcf file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   bcf_to_bed_ret_INVALID_FORMAT_2:
@@ -9924,10 +9923,10 @@ int32_t bed_from_23(char* infile_name, char* outname, char* outname_end, uint32_
   }
   if ((writebuf_cur == &(writebuf[3])) && (writebuf[0] == 'l')) {
     if (chrom_mask_23 == 0x7ffffff) {
-      logprint("Error: No --23file variants.\n");
+      logerrprint("Error: No --23file variants.\n");
       goto bed_from_23_ret_INVALID_FORMAT;
     } else {
-      logprint("Error: No --23file variants pass chromosome filter.\n");
+      logerrprint("Error: No --23file variants pass chromosome filter.\n");
       goto bed_from_23_ret_INVALID_CMDLINE;
     }
   }
@@ -9987,10 +9986,10 @@ int32_t bed_from_23(char* infile_name, char* outname, char* outname_end, uint32_
     if (y_present && (!nonmissing_y_present)) {
       if (x_present) {
 	if (!haploid_x_present) {
-	  logprint("Warning: No explicit haploid calls on X chromosome, and no nonmissing calls on\nY chromosome.  Double-check whether this is really a male sample.\n");
+	  logerrprint("Warning: No explicit haploid calls on X chromosome, and no nonmissing calls on\nY chromosome.  Double-check whether this is really a male sample.\n");
 	}
       } else {
-        logprint("Warning: No nonmissing calls on Y chromosome.  Double-check whether this is\nreally a male sample.\n");
+        logerrprint("Warning: No nonmissing calls on Y chromosome.  Double-check whether this is\nreally a male sample.\n");
       }
     }
   }
@@ -10011,13 +10010,13 @@ int32_t bed_from_23(char* infile_name, char* outname, char* outname_end, uint32_
     retval = RET_INVALID_CMDLINE;
     break;
   bed_from_23_ret_MISSING_TOKENS:
-    LOGPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, infile_name);
+    LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, infile_name);
     retval = RET_INVALID_FORMAT;
     break;
   bed_from_23_ret_MISSING_ALLELE_CALLS:
     LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer allele calls than expected.\n", line_idx, infile_name);
   bed_from_23_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   bed_from_23_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -10714,16 +10713,16 @@ int32_t simulate_dataset(char* outname, char* outname_end, uint32_t flags, char*
   while (fgets(tbuf, MAXLINELEN, infile)) {
     line_idx++;
     if (!tbuf[MAXLINELEN - 1]) {
-      sprintf(logbuf, "\nError: Line %" PRIuPTR " of --simulate%s file is pathologically long.\n", line_idx, is_qt? "-qt" : "");
-      goto simulate_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Line %" PRIuPTR " of --simulate%s file is pathologically long.\n", line_idx, is_qt? "-qt" : "");
+      goto simulate_ret_INVALID_FORMAT_2N;
     }
     cptr = skip_initial_spaces(tbuf);
     if (is_eoln_kns(*cptr)) {
       continue;
     }
     if (scan_uint_icap(cptr, &uii)) {
-      sprintf(logbuf, "\nError: Invalid SNP count on line %" PRIuPTR " of --simulate%s input file.\n", line_idx, is_qt? "-qt" : "");
-      goto simulate_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Invalid SNP count on line %" PRIuPTR " of --simulate%s input file.\n", line_idx, is_qt? "-qt" : "");
+      goto simulate_ret_INVALID_FORMAT_2N;
     }
     ullii += uii;
   }
@@ -10731,11 +10730,11 @@ int32_t simulate_dataset(char* outname, char* outname_end, uint32_t flags, char*
     goto simulate_ret_READ_FAIL;
   }
   if (!ullii) {
-    sprintf(logbuf, "\nError: --simulate%s input file specifies zero SNPs.\n", is_qt? "-qt" : "");
-    goto simulate_ret_INVALID_FORMAT_2;
+    sprintf(logbuf, "Error: --simulate%s input file specifies zero SNPs.\n", is_qt? "-qt" : "");
+    goto simulate_ret_INVALID_FORMAT_2N;
   } else if (ullii > (do_haps? 0x3fffffff : 0x7fffffff)) {
-    sprintf(logbuf, "\nError: --simulate%s input file specifies too many SNPs.\n", is_qt? "-qt" : "");
-    goto simulate_ret_INVALID_FORMAT_2;
+    sprintf(logbuf, "Error: --simulate%s input file specifies too many SNPs.\n", is_qt? "-qt" : "");
+    goto simulate_ret_INVALID_FORMAT_2N;
   }
   marker_ct = ullii;
   loop_end = (marker_ct + 99) / 100;
@@ -10759,12 +10758,12 @@ int32_t simulate_dataset(char* outname, char* outname_end, uint32_t flags, char*
     }
     last_ptr = next_token(penult_ptr);
     if (no_more_tokens(last_ptr)) {
-      sprintf(logbuf, "\nError: Line %" PRIuPTR " of --simulate%s file has fewer tokens than expected.\n", line_idx, is_qt? "-qt" : "");
-      goto simulate_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Line %" PRIuPTR " of --simulate%s file has fewer tokens than expected.\n", line_idx, is_qt? "-qt" : "");
+      goto simulate_ret_INVALID_FORMAT_2N;
     }
     if (!no_more_tokens(next_token(last_ptr))) {
-      sprintf(logbuf, "\nError: Line %" PRIuPTR " of --simulate%s file has more tokens than expected.\n", line_idx, is_qt? "-qt" : "");
-      goto simulate_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Line %" PRIuPTR " of --simulate%s file has more tokens than expected.\n", line_idx, is_qt? "-qt" : "");
+      goto simulate_ret_INVALID_FORMAT_2N;
     }
     scan_uint_icap(cptr, &uii);
     if (!uii) {
@@ -10775,55 +10774,56 @@ int32_t simulate_dataset(char* outname, char* outname_end, uint32_t flags, char*
     memcpy(cur_snp_label, snp_label_ptr, snp_label_len);
     cur_snp_label[snp_label_len++] = '_';
     if (scan_two_doubles(freq_lb_ptr, &freq_lb, &freq_delta) || (freq_lb < 0) || (freq_delta < freq_lb) || (freq_delta > 1)) {
-      sprintf(logbuf, "\nError: Invalid allele frequency bound on line %" PRIuPTR " of --simulate%s\nfile.\n", line_idx, is_qt? "-qt" : "");
-      goto simulate_ret_INVALID_FORMAT_2;
+      sprintf(logbuf, "Error: Invalid allele frequency bound on line %" PRIuPTR " of --simulate%s\nfile.\n", line_idx, is_qt? "-qt" : "");
+      goto simulate_ret_INVALID_FORMAT_2N;
     }
     freq_delta -= freq_lb;
     if (tags_or_haps) {
       if (scan_two_doubles(marker_freq_lb_ptr, &marker_freq_lb, &marker_freq_ub) || (marker_freq_lb < 0) || (marker_freq_ub < marker_freq_lb) || (marker_freq_ub > 1)) {
-	sprintf(logbuf, "\nError: Invalid marker allele frequency bound on line %" PRIuPTR " of\n--simulate%s file.\n", line_idx, is_qt? "-qt" : "");
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid marker allele frequency bound on line %" PRIuPTR " of\n--simulate%s file.\n", line_idx, is_qt? "-qt" : "");
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
       if (scan_double(marker_ld_ptr, &dprime) || (dprime < 0) || (dprime > 1)) {
-	sprintf(logbuf, "\nError: Invalid d-prime on line %" PRIuPTR " of --simulate%s input file.\n", line_idx, is_qt? "-qt" : "");
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid d-prime on line %" PRIuPTR " of --simulate%s input file.\n", line_idx, is_qt? "-qt" : "");
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
     } else {
       dprime = 1;
     }
     if (is_qt) {
       if (scan_double(penult_ptr, &qt_var) || (qt_var < 0) || (qt_var > 1)) {
-	sprintf(logbuf, "\nError: Invalid variance value on line %" PRIuPTR " of --simulate-qt file.\n", line_idx);
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid variance value on line %" PRIuPTR " of --simulate-qt file.\n", line_idx);
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
       if ((qt_var > 0) && (((freq_delta == 0) && ((freq_lb == 0) || (freq_lb == 1))) || (tags_or_haps && (marker_freq_lb == marker_freq_ub) && ((marker_freq_lb == 0) || (marker_freq_lb == 1))))) {
-	sprintf(logbuf, "\nError: Nonzero variance with fixed 0/1 allele frequency on line %" PRIuPTR " of\n--simulate-qt file.\n", line_idx);
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Nonzero variance with fixed 0/1 allele frequency on line %" PRIuPTR " of\n--simulate-qt file.\n", line_idx);
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
       qt_totvar += ((intptr_t)cur_marker_ct) * qt_var;
       if (qt_totvar > 1 + EPSILON) {
-	logprint("\nError: --simulate-qt input file specific QTL variance greater than 1.\n");
+	logprint("\n");
+	logerrprint("Error: --simulate-qt input file specific QTL variance greater than 1.\n");
 	goto simulate_ret_INVALID_FORMAT;
       }
       if (scan_double(last_ptr, &qt_dom)) {
-	sprintf(logbuf, "\nError: Invalid dominance deviation value on line %" PRIuPTR " of --simulate-qt\nfile.\n", line_idx);
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid dominance deviation value on line %" PRIuPTR " of --simulate-qt\nfile.\n", line_idx);
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
     } else {
       if (scan_double(penult_ptr, &het_odds) || (het_odds < 0)) {
-	sprintf(logbuf, "\nError: Invalid heterozygote disease odds ratio on line %" PRIuPTR " of\n--simulate file.\n", line_idx);
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid heterozygote disease odds ratio on line %" PRIuPTR " of\n--simulate file.\n", line_idx);
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
       if ((strlen_se(last_ptr) == 4) && match_upper_nt(last_ptr, "MULT", 4)) {
 	hom0_odds = het_odds * het_odds;
       } else if (scan_double(last_ptr, &hom0_odds) || (hom0_odds < 0)) {
-	sprintf(logbuf, "\nError: Invalid homozygote disease odds ratio on line %" PRIuPTR " of --simulate\nfile.\n", line_idx);
-	goto simulate_ret_INVALID_FORMAT_2;
+	sprintf(logbuf, "Error: Invalid homozygote disease odds ratio on line %" PRIuPTR " of --simulate\nfile.\n", line_idx);
+	goto simulate_ret_INVALID_FORMAT_2N;
       }
       if ((!zero_odds_ratio_warning_given) && ((het_odds == 0) || (hom0_odds == 0))) {
         putchar('\r');
 	logstr("\n");
-	logprint("Warning: Zero odds ratio present in --simulate input file.  Did you mean\n--simulate-qt instead?\n");
+	logerrprint("Warning: Zero odds ratio present in --simulate input file.  Did you mean\n--simulate-qt instead?\n");
 	zero_odds_ratio_warning_given = 1;
         printf("%u%%", pct);
       }
@@ -11172,8 +11172,9 @@ int32_t simulate_dataset(char* outname, char* outname_end, uint32_t flags, char*
   simulate_ret_WRITE_FAIL:
     retval = RET_WRITE_FAIL;
     break;
-  simulate_ret_INVALID_FORMAT_2:
-    logprintb();
+  simulate_ret_INVALID_FORMAT_2N:
+    logprint("\n");
+    logerrprintb();
   simulate_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -11258,7 +11259,7 @@ int32_t recode_allele_load(char* loadbuf, uintptr_t loadbuf_size, char* recode_a
     retval = RET_READ_FAIL;
     break;
   recode_allele_load_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
   }
  recode_allele_load_ret_1:
@@ -11675,12 +11676,18 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   char* pzwritep = NULL;
   uintptr_t unfiltered_marker_ctl = (unfiltered_marker_ct + (BITCT - 1)) / BITCT;
   uintptr_t unfiltered_sample_ct4 = (unfiltered_sample_ct + 3) / 4;
+  uintptr_t unfiltered_sample_ctl = (unfiltered_sample_ct + (BITCT - 1)) / BITCT;
   uintptr_t sample_ctv2 = 2 * ((sample_ct + (BITCT - 1)) / BITCT);
   uintptr_t final_mask = get_final_mask(sample_ct);
+  uintptr_t cur_final_mask = 0;
+  uintptr_t sample_ct_y = 0;
+  uintptr_t cur_sample_ct = 0;
   uintptr_t topsize = 0;
   unsigned char* wkspace_mark = wkspace_base;
   char delimiter = (recode_modifier & RECODE_TAB)? '\t' : ' ';
   uintptr_t* recode_allele_reverse = NULL;
+  uintptr_t* sample_exclude_y = NULL;
+  uintptr_t* cur_sample_exclude = NULL;
   char** mk_allele_ptrs = marker_allele_ptrs;
   char** allele_missing = NULL;
   char* recode_allele_extra = NULL;
@@ -11688,7 +11695,11 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   const char* missing_geno_ptr = g_missing_geno_ptr;
   char delim2 = delimiter;
   uintptr_t* sample_include2 = NULL;
+  uintptr_t* sample_include2_y = NULL;
+  uintptr_t* cur_sample_include2 = NULL;
   uintptr_t* sample_male_include2 = NULL;
+  uintptr_t* sample_male_include2_y = NULL;
+  uintptr_t* cur_sample_male_include2 = NULL;
   uint32_t lgen_ref = (recode_modifier & RECODE_LGEN_REF);
   uint32_t rlist = (recode_modifier & RECODE_RLIST);
   uint32_t beagle_nomap = (recode_modifier & RECODE_BEAGLE_NOMAP);
@@ -11701,6 +11712,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   uint32_t set_hh_missing = (misc_flags / MISC_SET_HH_MISSING) & 1;
   uint32_t real_ref_alleles = (misc_flags / MISC_REAL_REF_ALLELES) & 1;
   uint32_t xmhh_exists_orig = hh_exists & XMHH_EXISTS;
+  uint32_t omit_nonmale_y = recode_modifier & RECODE_OMIT_NONMALE_Y;
   uintptr_t header_len = 0;
   uintptr_t max_chrom_size = 0;
   uintptr_t max_fid_len = 0;
@@ -11714,6 +11726,8 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   uintptr_t* loadbuf_collapsed = NULL;
   uintptr_t* loadbuf_collapsed_end = NULL;
   char* sample_ids_collapsed = NULL;
+  char* sample_ids_collapsed_y = NULL;
+  char* cur_sample_ids_collapsed = NULL;
   char* writebuf = NULL;
   char* writebuf2 = NULL;
   char* writebuf3 = NULL;
@@ -11793,7 +11807,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   if (recode_012) {
     // may as well prevent user from shooting themselves in the foot here
     if ((recode_modifier & RECODE_01) && (output_missing_geno == '0') && (!(recode_modifier & (RECODE_A | RECODE_A_TRANSPOSE | RECODE_AD | RECODE_BIMBAM | RECODE_FASTPHASE | RECODE_FASTPHASE_1CHR | RECODE_STRUCTURE)))) {
-      logprint("Error: The --recode '01' modifier normally has to be used with a nonzero\n--output-missing-genotype setting.\n");
+      logerrprint("Error: The --recode '01' modifier normally has to be used with a nonzero\n--output-missing-genotype setting.\n");
       goto recode_ret_INVALID_CMDLINE;
     }
     mk_allele_ptrs = (char**)wkspace_alloc(unfiltered_marker_ct * 2 * sizeof(intptr_t));
@@ -11815,10 +11829,27 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     if (recode_modifier & (RECODE_LGEN | RECODE_LGEN_REF | RECODE_LIST | RECODE_RLIST)) {
       // need to collapse sample_ids to remove need for sample_uidx in inner
       // loop
-      sample_ids_collapsed = alloc_and_init_collapsed_arr(sample_ids, max_sample_id_len, unfiltered_sample_ct, sample_exclude, sample_ct, 1);
+      sample_ids_collapsed = alloc_and_init_collapsed_arr(sample_ids, max_sample_id_len, unfiltered_sample_ct, sample_exclude, sample_ct, (delimiter == '\t'));
       if (!sample_ids_collapsed) {
         goto recode_ret_NOMEM;
       }
+      if (omit_nonmale_y) {
+	if (wkspace_alloc_ul_checked(&sample_exclude_y, unfiltered_sample_ctl * sizeof(intptr_t))) {
+	  goto recode_ret_NOMEM;
+	}
+	memcpy(sample_exclude_y, sample_exclude, unfiltered_sample_ctl * sizeof(intptr_t));
+	bitfield_ornot(sample_exclude_y, sex_male, unfiltered_sample_ctl);
+	zero_trailing_bits(sample_exclude_y, unfiltered_sample_ct);
+	sample_ct_y = unfiltered_sample_ct - popcount_longs(sample_exclude_y, unfiltered_sample_ctl);
+        uii = 2 * ((sample_ct_y + (BITCT - 1)) / BITCT);
+	if (wkspace_alloc_ul_checked(&sample_include2_y, uii * sizeof(intptr_t)) ||
+            wkspace_alloc_ul_checked(&sample_male_include2_y, uii * sizeof(intptr_t))) {
+	  goto recode_ret_NOMEM;
+	}
+	fill_vec_55(sample_include2_y, sample_ct_y);
+	fill_vec_55(sample_male_include2_y, sample_ct_y);
+	sample_ids_collapsed_y = alloc_and_init_collapsed_arr(sample_ids, max_sample_id_len, unfiltered_sample_ct, sample_exclude_y, sample_ct_y, (delimiter == '\t'));
+      }
     }
   }
   if (recode_modifier & RECODE_VCF) {
@@ -11916,7 +11947,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     memcpy(cur_mk_allelesx[3], "    ", 4);
   } else if (recode_modifier & (RECODE_BIMBAM | RECODE_BIMBAM_1CHR)) {
     if (max_marker_allele_len != 2) {
-      logprint("Error: --recode bimbam cannot be used with multi-character allele names.\n");
+      logerrprint("Error: --recode bimbam cannot be used with multi-character allele names.\n");
       goto recode_ret_INVALID_FORMAT;
     }
     sample_uidx = 0;
@@ -11924,7 +11955,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
       next_unset_ul_unsafe_ck(sample_exclude, &sample_uidx);
       cptr = (char*)memchr(&(sample_ids[sample_uidx * max_sample_id_len]), '\t', max_sample_id_len);
       if (strchr(&(cptr[1]), ',')) {
-        logprint("Error: Comma present in sample ID during --recode bimbam run.\n");
+        logerrprint("Error: Comma present in sample ID during --recode bimbam run.\n");
         goto recode_ret_INVALID_FORMAT;
       }
     }
@@ -11932,7 +11963,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     for (marker_idx = 0; marker_idx < marker_ct; marker_uidx++, marker_idx++) {
       next_unset_ul_unsafe_ck(marker_exclude, &marker_uidx);
       if (strchr(&(marker_ids[marker_uidx * max_marker_id_len]), ',')) {
-        logprint("Error: Comma present in SNP ID during --recode bimbam run.\n");
+        logerrprint("Error: Comma present in SNP ID during --recode bimbam run.\n");
       }
     }
     // +1 because memcpyl3a() copies an extra character
@@ -11943,11 +11974,11 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   } else if (recode_modifier & (RECODE_FASTPHASE | RECODE_FASTPHASE_1CHR)) {
     max_chrom_size = get_max_chrom_size(chrom_info_ptr, marker_exclude, &last_chrom_fo_idx);
     if ((recode_modifier & RECODE_FASTPHASE_1CHR) && (max_chrom_size != marker_ct)) {
-      logprint("Error: --recode fastphase-1chr requires a single-chromosome dataset.  Did you\nmean '--recode fastphase'?  (Note the lack of a dash in the middle.)\n");
+      logerrprint("Error: --recode fastphase-1chr requires a single-chromosome dataset.  Did you\nmean '--recode fastphase'?  (Note the lack of a dash in the middle.)\n");
       goto recode_ret_INVALID_CMDLINE;
     }
     if (max_marker_allele_len != 2) {
-      logprint("Error: --recode fastphase cannot be used with multi-character allele names.\n(You can use the '01' or '12' modifier to work around this.)\n");
+      logerrprint("Error: --recode fastphase cannot be used with multi-character allele names.\n(You can use the '01' or '12' modifier to work around this.)\n");
       goto recode_ret_INVALID_CMDLINE;
     }
     if (recode_012) {
@@ -11990,7 +12021,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     // + sample_ct * max_sample_id_len + 1
     ulii = 3 + max_marker_id_len + 2 * max_marker_allele_len + sample_ct * max_sample_id_len;
     if ((recode_modifier & RECODE_LIST) && (max_marker_allele_len != 2)) {
-      logprint("Error: --recode list cannot be used with multi-character allele names.\n");
+      logerrprint("Error: --recode list cannot be used with multi-character allele names.\n");
       goto recode_ret_INVALID_FORMAT;
     }
     if (rlist) {
@@ -12005,10 +12036,10 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     writebufl[3] = &(writebuf[ulii * 3]);
   } else if (recode_modifier & RECODE_23) {
     if (sample_ct != 1) {
-      logprint("Error: --recode 23 can only be used on a file with exactly one sample.\n");
+      logerrprint("Error: --recode 23 can only be used on a file with exactly one sample.\n");
       goto recode_ret_INVALID_FORMAT;
     } else if (max_marker_allele_len != 2) {
-      logprint("Error: --recode 23 cannot be used with multi-character allele names.\n");
+      logerrprint("Error: --recode 23 cannot be used with multi-character allele names.\n");
       goto recode_ret_INVALID_FORMAT;
     }
     // chromosome code, marker position, single-char alleles
@@ -12094,14 +12125,14 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	  ulii = 6;
 	} else if (recode_modifier & (RECODE_A | RECODE_COMPOUND)) {
 	  if ((max_marker_allele_len != 2) && (recode_modifier & RECODE_COMPOUND)) {
-	    logprint("Error: --recode compound-genotypes cannot be used with multi-character allele\nnames.\n");
+	    logerrprint("Error: --recode compound-genotypes cannot be used with multi-character allele\nnames.\n");
 	    goto recode_ret_INVALID_FORMAT;
 	  }
 	  ulii = 3;
 	} else if (recode_modifier & (RECODE_HV | RECODE_HV_1CHR)) {
 	  max_chrom_size = get_max_chrom_size(chrom_info_ptr, marker_exclude, &last_chrom_fo_idx);
 	  if ((recode_modifier & RECODE_HV_1CHR) && (max_chrom_size != marker_ct)) {
-	    logprint("Error: --recode HV-1chr requires a single-chromosome dataset.  Did you mean\n'--recode HV'?  (Note the lack of a dash in the middle.)\n");
+	    logerrprint("Error: --recode HV-1chr requires a single-chromosome dataset.  Did you mean\n'--recode HV'?  (Note the lack of a dash in the middle.)\n");
 	    goto recode_ret_INVALID_CMDLINE;
 	  }
 	  if (max_marker_allele_len == 2) {
@@ -12316,7 +12347,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
       if (!shiftval) {
 	if (strchr(cptr, '_')) {
 	  shiftval = 1;
-	  logprint("Warning: Underscore(s) present in sample IDs.\n");
+	  logerrprint("Warning: Underscore(s) present in sample IDs.\n");
 	}
       }
       aptr = (char*)memchr(cptr, '\t', max_sample_id_len);
@@ -12462,7 +12493,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
       } else {
 	*cptr = '\0';
 	if (strchr(&(tbuf[13]), ':')) {
-	  logprint("Error: VCF chromosome codes may not include the ':' character.\n");
+	  logerrprint("Error: VCF chromosome codes may not include the ':' character.\n");
 	  goto recode_ret_INVALID_FORMAT;
 	}
         cptr = memcpya(cptr, ",length=", 8);
@@ -12508,7 +12539,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	  if (!shiftval) {
 	    if (strchr(cptr, '_')) {
 	      shiftval = 1;
-	      logprint("Warning: Underscore(s) present in sample IDs.\n");
+	      logerrprint("Warning: Underscore(s) present in sample IDs.\n");
 	    }
 	  }
 	  if (flexbputc_checked('_', output_bgz, outfile, bgz_outfile)) {
@@ -12865,7 +12896,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
       autosomal_marker_ct -= count_chrom_markers(chrom_info_ptr, chrom_info_ptr->xy_code, marker_exclude);
     }
     if (!autosomal_marker_ct) {
-      logprint("Error: No autosomal variants for --recode beagle.\n");
+      logerrprint("Error: No autosomal variants for --recode beagle.\n");
       goto recode_ret_ALL_MARKERS_EXCLUDED;
     }
     if (!beagle_nomap) {
@@ -12993,7 +13024,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     if (recode_modifier & RECODE_BIMBAM_1CHR) {
       ii = single_chrom_start(chrom_info_ptr, unfiltered_marker_ct, marker_exclude);
       if (ii == -1) {
-        logprint("Error: --recode bimbam-1chr requires a single-chromosome dataset.  Did you mean\n'--recode bimbam'?  (Note the lack of a dash in the middle.)\n");
+        logerrprint("Error: --recode bimbam-1chr requires a single-chromosome dataset.  Did you mean\n'--recode bimbam'?  (Note the lack of a dash in the middle.)\n");
         goto recode_ret_INVALID_CMDLINE;
       }
       marker_uidx = ((uint32_t)ii);
@@ -13520,13 +13551,19 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
       }
     }
   } else if (recode_modifier & (RECODE_LIST | RECODE_RLIST)) {
-    // todo: fix
     strcpy(outname_end, rlist? ".rlist" : ".list");
     if (fopen_checked(&outfile, outname, "w")) {
       goto recode_ret_OPEN_FAIL;
     }
     if (delimiter != '\t') {
-      sample_delim_convert(unfiltered_sample_ct, sample_exclude, sample_ct, sample_ids, max_sample_id_len, '\t', ' ');
+      if (wkspace_alloc_ul_checked(&ulptr, (sample_ctv2 / 2) * sizeof(intptr_t))) {
+	goto recode_ret_NOMEM;
+      }
+      fill_ulong_zero(ulptr, sample_ctv2 / 2);
+      sample_delim_convert(sample_ct, ulptr, sample_ct, sample_ids_collapsed, max_sample_id_len, '\t', ' ');
+      if (omit_nonmale_y) {
+        sample_delim_convert(sample_ct_y, ulptr, sample_ct_y, sample_ids_collapsed_y, max_sample_id_len, '\t', ' ');
+      }
     }
     if (rlist) {
       *outname_end = '\0';
@@ -13542,6 +13579,8 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     cur_mk_allelesx[1][1] = delimiter;
     cur_mk_allelesx[1][2] = missing_geno;
     cmalen[1] = 3;
+    chrom_fo_idx = 0xffffffffU;
+    chrom_end = 0;
     for (pct = 1; pct <= 100; pct++) {
       loop_end = (((uint64_t)pct) * marker_ct) / 100;
       for (; marker_idx < loop_end; marker_uidx++, marker_idx++) {
@@ -13554,13 +13593,28 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	if (marker_uidx >= chrom_end) {
 	  chrom_fo_idx++;
 	  refresh_chrom_info(chrom_info_ptr, marker_uidx, &chrom_end, &chrom_fo_idx, &is_x, &is_y, &is_mt, &is_haploid);
+	  if (omit_nonmale_y && is_y) {
+	    cur_final_mask = get_final_mask(sample_ct_y);
+	    cur_sample_ct = sample_ct_y;
+	    cur_sample_exclude = sample_exclude_y;
+	    cur_sample_ids_collapsed = sample_ids_collapsed_y;
+	    cur_sample_include2 = sample_include2_y;
+	    cur_sample_male_include2 = sample_male_include2_y;
+	  } else {
+	    cur_final_mask = final_mask;
+	    cur_sample_ct = sample_ct;
+	    cur_sample_exclude = sample_exclude;
+	    cur_sample_ids_collapsed = sample_ids_collapsed;
+	    cur_sample_include2 = sample_include2;
+	    cur_sample_male_include2 = sample_male_include2;
+	  }
 	  chrom_idx = chrom_info_ptr->chrom_file_order[chrom_fo_idx];
 	}
-	if (load_and_collapse(bedfile, (uintptr_t*)loadbuf, unfiltered_sample_ct, loadbuf_collapsed, sample_ct, sample_exclude, final_mask, IS_SET(marker_reverse, marker_uidx))) {
+	if (load_and_collapse(bedfile, (uintptr_t*)loadbuf, unfiltered_sample_ct, loadbuf_collapsed, cur_sample_ct, cur_sample_exclude, cur_final_mask, IS_SET(marker_reverse, marker_uidx))) {
 	  goto recode_ret_READ_FAIL;
 	}
 	if (is_haploid && set_hh_missing) {
-          haploid_fix(hh_exists, sample_include2, sample_male_include2, sample_ct, is_x, is_y, (unsigned char*)loadbuf_collapsed);
+          haploid_fix(hh_exists, cur_sample_include2, cur_sample_male_include2, cur_sample_ct, is_x, is_y, (unsigned char*)loadbuf_collapsed);
 	}
 	init_recode_cmax(mk_allele_ptrs[2 * marker_uidx], mk_allele_ptrs[2 * marker_uidx + 1], cur_mk_allelesx, cmalen, '\0', delimiter);
 	cmalen[0] -= 1;
@@ -13597,7 +13651,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	  writebuflp[ulii] = wbufptr;
 	}
 	ulptr = loadbuf_collapsed;
-	ulptr_end = &(loadbuf_collapsed[sample_ct / BITCT2]);
+	ulptr_end = &(loadbuf_collapsed[cur_sample_ct / BITCT2]);
 	sample_idx = 0;
 	sample_uidx = BITCT2; // repurposed as stop value
 	if (rlist) {
@@ -13611,7 +13665,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
                 ulii = cur_word & 3;
 		if (ulii != 3) {
 		  *(writebuflp[ulii]++) = delimiter;
-		  writebuflp[ulii] = strcpya(writebuflp[ulii], &(sample_ids_collapsed[sample_idx * max_sample_id_len]));
+		  writebuflp[ulii] = strcpya(writebuflp[ulii], &(cur_sample_ids_collapsed[sample_idx * max_sample_id_len]));
 		}
 	      }
 	      sample_uidx += BITCT2;
@@ -13620,7 +13674,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	      break;
 	    }
             ulptr_end++;
-	    sample_uidx = sample_ct;
+	    sample_uidx = cur_sample_ct;
 	  }
 	  if (writebuflp[2] != writebuflps[2]) {
 	    *(writebuflp[2]++) = '\n';
@@ -13647,7 +13701,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	      for (; sample_idx < sample_uidx; sample_idx++, cur_word >>= 2) {
                 ulii = cur_word & 3;
 		*(writebuflp[ulii]++) = delimiter;
-		writebuflp[ulii] = strcpya(writebuflp[ulii], &(sample_ids_collapsed[sample_idx * max_sample_id_len]));
+		writebuflp[ulii] = strcpya(writebuflp[ulii], &(cur_sample_ids_collapsed[sample_idx * max_sample_id_len]));
 	      }
 	      sample_uidx += BITCT2;
 	    }
@@ -13655,7 +13709,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	      break;
 	    }
             ulptr_end++;
-	    sample_uidx = sample_ct;
+	    sample_uidx = cur_sample_ct;
 	  }
 	  *(writebuflp[0]++) = '\n';
 	  *(writebuflp[1]++) = '\n';
@@ -13683,9 +13737,6 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
 	fflush(stdout);
       }
     }
-    if (delimiter != '\t') {
-      sample_delim_convert(unfiltered_sample_ct, sample_exclude, sample_ct, sample_ids, max_sample_id_len, ' ', '\t');
-    }
   } else if (recode_modifier & (RECODE_HV | RECODE_HV_1CHR)) {
     if (wkspace_left < ((uint64_t)unfiltered_sample_ct4) * max_chrom_size) {
       goto recode_ret_NO_MULTIPASS_YET;
@@ -13916,7 +13967,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
   if (!(recode_modifier & (RECODE_BEAGLE | RECODE_FASTPHASE | RECODE_FASTPHASE_1CHR | RECODE_HV))) {
     logprint("done.\n");
     if (invalid_allele_code_seen) {
-      logprint("Warning: At least one VCF allele code violates the official specification;\nother tools may not accept the file.  (Valid codes must either start with a\n'<', only contain characters in {A,C,G,T,N,a,c,g,t,n}, or represent a\nbreakend.)\n");
+      logerrprint("Warning: At least one VCF allele code violates the official specification;\nother tools may not accept the file.  (Valid codes must either start with a\n'<', only contain characters in {A,C,G,T,N,a,c,g,t,n}, or represent a\nbreakend.)\n");
     }
   } else {
     fputs("done.\n", stdout);
@@ -13950,7 +14001,7 @@ int32_t recode(uint32_t recode_modifier, FILE* bedfile, uintptr_t bed_offset, ch
     break;
   recode_ret_NO_MULTIPASS_YET:
     // probably want to implement this later
-    logprint("Error: --recode does not yet support multipass recoding of very large files;\ncontact the " PROG_NAME_CAPS " developers if you need this.\nFor now, you can try using a machine with more memory, and/or split the file\ninto smaller pieces and recode them separately.\n");
+    logerrprint("Error: --recode does not yet support multipass recoding of very large files;\ncontact the " PROG_NAME_CAPS " developers if you need this.\nFor now, you can try using a machine with more memory, and/or split the file\ninto smaller pieces and recode them separately.\n");
     retval = RET_CALC_NOT_YET_SUPPORTED;
     break;
   recode_ret_ALL_MARKERS_EXCLUDED:
@@ -14044,7 +14095,7 @@ int32_t sample_sort_file_map(char* sample_sort_fname, uintptr_t unfiltered_sampl
     goto sample_sort_file_map_ret_READ_FAIL;
   }
   if (cur_seq != sample_ct) {
-    logprint("Error: --indiv-sort file does not contain all loaded sample IDs.\n");
+    logerrprint("Error: --indiv-sort file does not contain all loaded sample IDs.\n");
     goto sample_sort_file_map_ret_INVALID_CMDLINE;
   }
   *sample_sort_map_ptr = sample_sort_map;
@@ -14060,7 +14111,7 @@ int32_t sample_sort_file_map(char* sample_sort_fname, uintptr_t unfiltered_sampl
     retval = RET_READ_FAIL;
     break;
   sample_sort_file_map_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   sample_sort_file_map_ret_INVALID_CMDLINE:
@@ -14162,9 +14213,9 @@ int32_t merge_fam_id_scan(char* bedname, char* famname, uintptr_t* max_sample_id
     if ((!orig_idx) && (uii > 8) && ((!memcmp(&(famname[uii - 8]), ".bed.fam", 8)) || (!memcmp(&(famname[uii - 8]), ".bim.fam", 8)) || (!memcmp(&(famname[uii - 8]), ".fam.fam", 8)))) {
       // technically could be --merge-list with no --bfile, but we won't bother
       // with a specialized error message for that case.
-      LOGPRINTFWW("Error: Failed to open %s. (--bfile expects a filename *prefix*; '.bed', '.bim', and '.fam' are automatically appended.)\n", famname);
+      LOGERRPRINTFWW("Error: Failed to open %s. (--bfile expects a filename *prefix*; '.bed', '.bim', and '.fam' are automatically appended.)\n", famname);
     } else {
-      LOGPRINTFWW(errstr_fopen, famname);
+      LOGERRPRINTFWW(errstr_fopen, famname);
     }
     goto merge_fam_id_scan_ret_OPEN_FAIL;
   }
@@ -14188,6 +14239,15 @@ int32_t merge_fam_id_scan(char* bedname, char* famname, uintptr_t* max_sample_id
       col6_start_ptr = skip_initial_spaces(&(col5_start_ptr[uii]));
       if (no_more_tokens_kns(col6_start_ptr)) {
 	LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, famname);
+	if (text_file) {
+	  // .ped/.map swap?  give a more helpful error message then
+	  uii = strlen(famname);
+	  if (!memcmp(&(famname[uii - 4]), ".map", 4)) {
+	    logprintb();
+	    logprint("(The .ped file must be named before the .map; did you swap them?)\n");
+	    goto merge_fam_id_scan_ret_INVALID_FORMAT;
+	  }
+	}
 	goto merge_fam_id_scan_ret_INVALID_FORMAT_2;
       }
       if (uii != 1) {
@@ -14296,7 +14356,8 @@ int32_t merge_fam_id_scan(char* bedname, char* famname, uintptr_t* max_sample_id
   merge_fam_id_scan_ret_LONG_LINE:
     LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, famname);
   merge_fam_id_scan_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
+  merge_fam_id_scan_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
   }
   fclose_cond(infile);
@@ -14307,14 +14368,9 @@ int32_t merge_sample_sortf(char* sample_sort_fname, char* sample_fids, uintptr_t
   // sample_fids[] is already sorted
   unsigned char* wkspace_mark = wkspace_base;
   int32_t retval = 0;
-  char* sptr;
-  char* sptr2;
   uintptr_t sample_uidx;
   for (sample_uidx = 0; sample_uidx < tot_sample_ct; sample_uidx++) {
-    sptr = &(sample_fids[sample_uidx * max_sample_full_len]);
-    sptr2 = (char*)memchr(sptr, '\t', max_sample_id_len);
-    sptr2 = (char*)memchr(&(sptr2[1]), '\t', max_sample_id_len);
-    memcpyx(&(sample_ids[sample_uidx * max_sample_id_len]), sptr, (uintptr_t)(sptr2 - sptr), '\0');
+    strcpy(&(sample_ids[sample_uidx * max_sample_id_len]), &(sample_fids[sample_uidx * max_sample_full_len]));
   }
   retval = sample_sort_file_map(sample_sort_fname, tot_sample_ct, NULL, tot_sample_ct, sample_ids, max_sample_id_len, &map_reverse);
   wkspace_reset(wkspace_mark);
@@ -14528,13 +14584,13 @@ int32_t merge_bim_scan(char* bimname, uint32_t is_binary, uintptr_t* max_marker_
 	    if ((((uint64_t)ll_ptr->pos) >> 32) == (((uint64_t)llxx) >> 32)) {
 	      LOGPREPRINTFWW("Warning: Multiple positions seen for variant '%s'.\n", bufptr);
 	      if (position_warning_ct < 3) {
-		logprintb();
+		logerrprintb();
 	      } else {
 		logstr(logbuf);
 	      }
 	      position_warning_ct++;
 	    } else {
-	      LOGPRINTFWW("Warning: Multiple chromosomes seen for variant '%s'.\n", bufptr);
+	      LOGERRPRINTFWW("Warning: Multiple chromosomes seen for variant '%s'.\n", bufptr);
 	    }
 	  }
 	  name_match = 1;
@@ -14602,7 +14658,7 @@ int32_t merge_bim_scan(char* bimname, uint32_t is_binary, uintptr_t* max_marker_
   merge_bim_scan_ret_MISSING_TOKENS:
     LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, bimname);
   merge_bim_scan_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   merge_bim_scan_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
   }
@@ -14667,7 +14723,7 @@ int32_t report_non_biallelics(char* outname, char* outname_end, Ll_str* non_bial
   if (fclose_null(&outfile)) {
     goto report_non_biallelics_ret_WRITE_FAIL;
   }
-  LOGPRINTF("Error: %" PRIuPTR " variant%s with 3+ alleles present.\n* If you believe this is due to strand inconsistency, try --flip with\n  %s.\n  (Warning: if this seems to work, strand errors involving SNPs with A/T or C/G\n  alleles probably remain in your data.  If LD between nearby SNPs is high,\n  --flip-scan should detect them.)\n* If you are dealing with genuine multiallelic variants, we recommend exporting\n  that subset of the data to VCF (via e.g. '--recode vcf'), merging wi [...]
+  LOGERRPRINTF("Error: %" PRIuPTR " variant%s with 3+ alleles present.\n* If you believe this is due to strand inconsistency, try --flip with\n  %s.\n  (Warning: if this seems to work, strand errors involving SNPs with A/T or C/G\n  alleles probably remain in your data.  If LD between nearby SNPs is high,\n  --flip-scan should detect them.)\n* If you are dealing with genuine multiallelic variants, we recommend exporting\n  that subset of the data to VCF (via e.g. '--recode vcf'), merging [...]
   while (0) {
   report_non_biallelics_ret_NOMEM:
     retval = RET_NOMEM;
@@ -14773,12 +14829,12 @@ static inline uint32_t merge_post_msort_update_maps(char* marker_ids, uintptr_t
       // do not merge chr 0 (unplaced).
       if ((prev_bp == cur_bp) && (!unplaced)) {
 	if (merge_equal_pos && merge_alleles(marker_allele_ptrs, ((uint32_t)ll_buf[read_pos - 1]), presort_idx)) {
-	  LOGPRINTFWW("Error: --merge-equal-pos failure.  Variants '%s' and '%s' have the same position, but do not share the same alleles.\n", &(marker_ids[max_marker_id_len * presort_idx]), &(marker_ids[max_marker_id_len * ((uint32_t)ll_buf[read_pos - 1])]));
+	  LOGERRPRINTFWW("Error: --merge-equal-pos failure.  Variants '%s' and '%s' have the same position, but do not share the same alleles.\n", &(marker_ids[max_marker_id_len * presort_idx]), &(marker_ids[max_marker_id_len * ((uint32_t)ll_buf[read_pos - 1])]));
 	  return 1;
 	}
 	LOGPREPRINTFWW("Warning: Variants '%s' and '%s' have the same position.\n", &(marker_ids[max_marker_id_len * presort_idx]), &(marker_ids[max_marker_id_len * ((uint32_t)ll_buf[read_pos - 1])]));
 	if (position_warning_ct < 3) {
-	  logprintb();
+	  logerrprintb();
 	} else {
 	  logstr(logbuf);
 	}
@@ -14798,7 +14854,7 @@ static inline uint32_t merge_post_msort_update_maps(char* marker_ids, uintptr_t
     chrom_start[chrom_idx + 1] = write_pos;
   }
   if (position_warning_ct > 3) {
-    printf("%u more same-position warning%s: see log file.\n", position_warning_ct - 3, (position_warning_ct == 4)? "" : "s");
+    fprintf(stderr, "%u more same-position warning%s: see log file.\n", position_warning_ct - 3, (position_warning_ct == 4)? "" : "s");
   }
   *dedup_marker_ct_ptr = write_pos;
   return 0;
@@ -15474,20 +15530,20 @@ int32_t merge_main(char* bedname, char* bimname, char* famname, char* bim_loadbu
     retval = RET_WRITE_FAIL;
     break;
   merge_main_ret_NOT_BIALLELIC:
-    putchar('\n');
-    LOGPRINTFWW("Error: Variant '%s' is not biallelic. To obtain a full list of merge failures, convert your data to binary format and retry the merge.\n", &(marker_ids[uii * max_marker_id_len]));
+    logprint("\n");
+    LOGERRPRINTFWW("Error: Variant '%s' is not biallelic. To obtain a full list of merge failures, convert your data to binary format and retry the merge.\n", &(marker_ids[uii * max_marker_id_len]));
     retval = RET_INVALID_FORMAT;
     break;
   merge_main_ret_HALF_MISSING:
-    putchar('\n');
-    LOGPRINTFWW("Error: Line %" PRIuPTR " of %s has a half-missing call.\n", line_idx, bedname);
+    logprint("\n");
+    LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s has a half-missing call.\n", line_idx, bedname);
     retval = RET_INVALID_FORMAT;
     break;
   merge_main_ret_MISSING_TOKENS:
     LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, bedname);
   merge_main_ret_INVALID_FORMAT_2N:
-    putchar('\n');
-    logprintb();
+    logprint("\n");
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -15642,13 +15698,13 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
       goto merge_datasets_ret_READ_FAIL;
     }
     if (!merge_ct) {
-      logprint("Error: --merge-list file is empty, and no other input fileset was specified.\n");
+      logerrprint("Error: --merge-list file is empty, and no other input fileset was specified.\n");
       goto merge_datasets_ret_INVALID_FORMAT;
     } else if (merge_ct == 1) {
       if (famname[0] == '\0') {
-        logprint("Warning: --merge-list file contains only one entry.\n");
+        logerrprint("Warning: --merge-list file contains only one entry.\n");
       } else {
-        logprint("Warning: --merge-list file is empty.\n");
+        logerrprint("Warning: --merge-list file is empty.\n");
       }
     }
 #ifndef __LP64__
@@ -15810,8 +15866,11 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
 	} while (ll_ptr);
       }
     }
-    for (uii = 0; uii < tot_sample_ct; uii++) {
-      sample_nsmap[uii] = uii;
+    for (ulii = 0; ulii < tot_sample_ct; ulii++) {
+      sample_nsmap[ulii] = ulii;
+      bufptr = (char*)memchr(&(sample_fids[ulii * max_sample_full_len]), '\t', max_sample_id_len);
+      bufptr = (char*)memchr(&(bufptr[1]), '\t', max_sample_id_len);
+      *bufptr = '\0';
     }
     if (qsort_ext(sample_fids, tot_sample_ct, max_sample_full_len, strcmp_deref, (char*)sample_nsmap, sizeof(int32_t))) {
       goto merge_datasets_ret_NOMEM2;
@@ -15839,6 +15898,15 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
 	} while (ll_ptr);
       }
     }
+    // bugfix: parental IDs and phenotype were being used to break sorting
+    // ties here, so e.g. "CEU NA07000 0 0 2" was being natural-sorted after
+    // "ceu NA07000 0 0 1", resulting in a crash when "CEU NA07000" was looked
+    // up later.
+    for (ulii = 0; ulii < tot_sample_ct; ulii++) {
+      bufptr = (char*)memchr(&(sample_fids[ulii * max_sample_full_len]), '\t', max_sample_id_len);
+      bufptr = (char*)memchr(&(bufptr[1]), '\t', max_sample_id_len);
+      *bufptr = '\0';
+    }
     if (is_dichot_pheno) {
       if (qsort_ext(sample_fids, tot_sample_ct, max_sample_full_len, merge_nsort? strcmp_natural_deref : strcmp_deref, pheno_c_char, 1)) {
 	goto merge_datasets_ret_NOMEM2;
@@ -15871,12 +15939,12 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
       ujj = map_reverse[ulii];
       bufptr = &(sample_fids[ujj * max_sample_full_len]);
       bufptr3 = &(sample_ids[ujj * max_sample_id_len]);
-      bufptr2 = (char*)memchr(bufptr, '\t', max_sample_id_len);
-      bufptr2 = (char*)memchr(&(bufptr2[1]), '\t', max_sample_id_len);
-      uii = (uintptr_t)(bufptr2 - bufptr);
-      memcpyx(bufptr3, bufptr, uii, '\0');
+      uii = strlen(bufptr) + 1;
+      memcpy(bufptr3, bufptr, uii);
       if (merge_mode < 6) {
-	uii += strlen(bufptr2);
+        // no longer matters whether this is \t or \0, we're about to free it
+	bufptr[uii - 1] = '\t';
+	uii += strlen(&(bufptr[uii]));
 	if (fwrite_checked(bufptr, uii, outfile)) {
 	  goto merge_datasets_ret_WRITE_FAIL;
 	}
@@ -15892,14 +15960,15 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
     bufptr = sample_fids;
     bufptr3 = sample_ids;
     for (ulii = 0; ulii < tot_sample_ct; ulii++) {
-      bufptr2 = (char*)memchr(bufptr, '\t', max_sample_id_len);
-      bufptr2 = (char*)memchr(&(bufptr2[1]), '\t', max_sample_id_len);
-      uii = (uintptr_t)(bufptr2 - bufptr);
-      memcpyx(bufptr3, bufptr, uii, '\0');
+      uii = strlen(bufptr) + 1;
+      memcpy(bufptr3, bufptr, uii);
       bufptr3 = &(bufptr3[max_sample_id_len]);
       if (merge_mode < 6) {
-	uii += strlen(bufptr2);
-	fwrite(bufptr, 1, uii, outfile);
+	bufptr[uii - 1] = '\t';
+	uii += strlen(&(bufptr[uii]));
+	if (fwrite_checked(bufptr, uii, outfile)) {
+	  goto merge_datasets_ret_WRITE_FAIL;
+	}
 	if (is_dichot_pheno) {
 	  cc = pheno_c_char[ulii];
 	  fprintf(outfile, "\t%s\n", cc? ((cc == 1)? "2" : "-9") : "1");
@@ -15916,7 +15985,9 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
       for (ulii = 0; ulii < tot_sample_ct; ulii++) {
 	ujj = map_reverse[ulii];
 	bufptr = &(sample_fids[ujj * max_sample_full_len]);
-	if (fwrite_checked(bufptr, strlen(bufptr), outfile)) {
+	uii = strlen(bufptr);
+	bufptr[uii] = '\t';
+	if (fwrite_checked(bufptr, uii + strlen(&(bufptr[uii])), outfile)) {
 	  goto merge_datasets_ret_WRITE_FAIL;
 	}
 	if (is_dichot_pheno) {
@@ -15972,16 +16043,16 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
     }
   }
   if (position_warning_ct > 3) {
-    printf("%" PRIu64 " more multiple-position warning%s: see log file.\n", position_warning_ct - 3, (position_warning_ct == 4)? "" : "s");
+    fprintf(stderr, "%" PRIu64 " more multiple-position warning%s: see log file.\n", position_warning_ct - 3, (position_warning_ct == 4)? "" : "s");
   }
 #ifdef __LP64__
   if (ullxx > 0x7fffffff) {
-    logprint("Error: Too many variants (max 2147483647).\n");
+    logerrprint("Error: Too many variants (max 2147483647).\n");
     goto merge_datasets_ret_INVALID_FORMAT;
   }
 #else
   if (ullxx * MAXV(max_marker_id_len, 8) > 0x7fffffff) {
-    logprint("Error: Too many variants for 32-bit " PROG_NAME_CAPS ".\n");
+    logerrprint("Error: Too many variants for 32-bit " PROG_NAME_CAPS ".\n");
     goto merge_datasets_ret_INVALID_FORMAT;
   }
 #endif
@@ -16078,7 +16149,7 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
     goto merge_datasets_ret_INVALID_FORMAT;
   }
   if (!dedup_marker_ct) {
-    logprint("Error: No variants in merged file.\n");
+    logerrprint("Error: No variants in merged file.\n");
     goto merge_datasets_ret_INVALID_FORMAT;
   }
   wkspace_reset((char*)marker_cms_tmp);
@@ -16270,7 +16341,7 @@ int32_t merge_datasets(char* bedname, char* bimname, char* famname, char* outnam
     retval = RET_WRITE_FAIL;
     break;
   merge_datasets_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   merge_datasets_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
diff --git a/plink_dosage.c b/plink_dosage.c
index 861048f..602a504 100644
--- a/plink_dosage.c
+++ b/plink_dosage.c
@@ -170,11 +170,11 @@ int32_t dosage_load_score_files(Score_info* sc_ip, char* outname, char* outname_
     }
   }
   if (!score_marker_ct) {
-    logprint("Error: No valid entries in --score file.\n");
+    logerrprint("Error: No valid entries in --score file.\n");
     goto dosage_load_score_files_ret_INVALID_FORMAT;
   }
   if (score_marker_ct >= 0x40000000) {
-    logprint("Error: --score does not support >= 2^30 variants.\n");
+    logerrprint("Error: --score does not support >= 2^30 variants.\n");
     goto dosage_load_score_files_ret_INVALID_FORMAT;
   }
 #ifndef __LP64__
@@ -294,7 +294,7 @@ int32_t dosage_load_score_files(Score_info* sc_ip, char* outname, char* outname_
   }
   LOGPRINTFWW("--score: %" PRIuPTR " entr%s loaded from %s.\n", score_marker_ct, (score_marker_ct == 1)? "y" : "ies", sc_ip->fname);
   if (miss_ct) {
-    LOGPRINTF("Warning: %" PRIuPTR " line%s skipped.\n", miss_ct, (miss_ct == 1)? "" : "s");
+    LOGERRPRINTF("Warning: %" PRIuPTR " line%s skipped.\n", miss_ct, (miss_ct == 1)? "" : "s");
   }
   *score_marker_ct_ptr = score_marker_ct;
   *max_score_marker_id_len_ptr = max_score_marker_id_len;
@@ -359,7 +359,7 @@ int32_t dosage_load_score_files(Score_info* sc_ip, char* outname, char* outname_
       goto dosage_load_score_files_ret_READ_FAIL;
     }
     if (miss_ct) {
-      LOGPRINTF("Warning: %" PRIuPTR " line%s skipped in --q-score-range data file.\n", miss_ct, (miss_ct == 1)? "" : "s");
+      LOGERRPRINTF("Warning: %" PRIuPTR " line%s skipped in --q-score-range data file.\n", miss_ct, (miss_ct == 1)? "" : "s");
     }
     if (fopen_checked(&infile, sc_ip->range_fname, "r")) {
       goto dosage_load_score_files_ret_OPEN_FAIL;
@@ -396,7 +396,7 @@ int32_t dosage_load_score_files(Score_info* sc_ip, char* outname, char* outname_
       goto dosage_load_score_files_ret_READ_FAIL;
     }
     if (!qrange_ct) {
-      logprint("Error: No valid entries in --q-score-range range file.\n");
+      logerrprint("Error: No valid entries in --q-score-range range file.\n");
       goto dosage_load_score_files_ret_INVALID_FORMAT;
     }
     if (wkspace_alloc_c_checked(score_qrange_names_ptr, qrange_ct * max_qrange_name_len) ||
@@ -445,13 +445,13 @@ int32_t dosage_load_score_files(Score_info* sc_ip, char* outname, char* outname_
     retval = RET_READ_FAIL;
     break;
   dosage_load_score_files_ret_MISSING_TOKENS_Q:
-    LOGPRINTF("Error: Line %" PRIuPTR " of --q-score-range data file has fewer tokens than\nexpected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of --q-score-range data file has fewer tokens than\nexpected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   dosage_load_score_files_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --score file has fewer tokens than expected.\n", line_idx);
   dosage_load_score_files_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   dosage_load_score_files_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -461,7 +461,7 @@ int32_t dosage_load_score_files(Score_info* sc_ip, char* outname, char* outname_
   return retval;
 }
 
-int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* outname, char* outname_end, char* phenoname, char* extractname, char* excludename, char* keepname, char* removename, char* keepfamname, char* removefamname, char* filtername, char* makepheno_str, char* phenoname_str, char* covar_fname, Two_col_params* qual_filter, Two_col_params* update_map, Two_col_params* update_name, char* update_ids_fname, char* update_parents_fname, char* update_sex_fname, char* filtervals_ [...]
+int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* outname, char* outname_end, char* phenoname, char* extractname, char* excludename, char* keepname, char* removename, char* keepfamname, char* removefamname, char* filtername, char* makepheno_str, char* phenoname_str, char* covar_fname, Two_col_params* qual_filter, Two_col_params* update_map, Two_col_params* update_name, char* update_ids_fname, char* update_parents_fname, char* update_sex_fname, char* filtervals_ [...]
   // sucks to duplicate so much, but this code will be thrown out later so
   // there's no long-term maintenance problem
   FILE* phenofile = NULL;
@@ -678,7 +678,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
       goto plink1_dosage_ret_1;
     }
     if (map_is_unsorted & UNSORTED_SPLIT_CHROM) {
-      logprint("Error: .map file has a split chromosome.\n");
+      logerrprint("Error: .map file has a split chromosome.\n");
       goto plink1_dosage_ret_INVALID_FORMAT;
     }
   }
@@ -792,7 +792,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 	  }
 	} else {
 	  if (map_is_unsorted & UNSORTED_BP) {
-	    logprint("Error: '--extract range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
+	    logerrprint("Error: '--extract range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
 	    goto plink1_dosage_ret_INVALID_CMDLINE;
 	  }
 	  retval = extract_exclude_range(extractname, marker_pos, unfiltered_marker_ct, marker_exclude, &marker_exclude_ct, 0, chrom_info_ptr);
@@ -811,7 +811,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 	  }
 	} else {
 	  if (map_is_unsorted & UNSORTED_BP) {
-	    logprint("Error: '--exclude range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
+	    logerrprint("Error: '--exclude range' requires a sorted .bim.  Retry this command after\nusing --make-bed to sort your data.\n");
 	    goto plink1_dosage_ret_INVALID_CMDLINE;
 	  }
 	  retval = extract_exclude_range(excludename, marker_pos, unfiltered_marker_ct, marker_exclude, &marker_exclude_ct, 1, chrom_info_ptr);
@@ -918,7 +918,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     uii = popcount_longs_exclude(pheno_nm, sex_nm, unfiltered_sample_ctl);
     if (uii) {
       bitfield_and(pheno_nm, sex_nm, unfiltered_sample_ctl);
-      logprint("Warning: Ignoring phenotypes of missing-sex samples.  If you don't want those\nphenotypes to be ignored, use the --allow-no-sex flag.\n");
+      logerrprint("Warning: Ignoring phenotypes of missing-sex samples.  If you don't want those\nphenotypes to be ignored, use the --allow-no-sex flag.\n");
     }
   }
   if (do_glm || (filter_flags & FILTER_PRUNE)) {
@@ -928,7 +928,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     sample_exclude_ct = popcount_longs(sample_exclude, unfiltered_sample_ctl);
     uii = do_glm && (!(filter_flags & FILTER_PRUNE));
     if (sample_exclude_ct == unfiltered_sample_ct) {
-      LOGPRINTF("Error: All %s removed by %s--prune.\n", g_species_plural, uii? "automatic " : "");
+      LOGERRPRINTF("Error: All %s removed by %s--prune.\n", g_species_plural, uii? "automatic " : "");
       goto plink1_dosage_ret_ALL_SAMPLES_EXCLUDED;
     }
     if ((filter_flags & FILTER_PRUNE) || (sample_exclude_ct != ulii)) {
@@ -938,13 +938,13 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 
   if (filter_flags & (FILTER_BINARY_CASES | FILTER_BINARY_CONTROLS)) {
     if (!pheno_c) {
-      logprint("Error: --filter-cases/--filter-controls requires a case/control phenotype.\n");
+      logerrprint("Error: --filter-cases/--filter-controls requires a case/control phenotype.\n");
       goto plink1_dosage_ret_INVALID_CMDLINE;
     }
     ii = sample_exclude_ct;
     filter_samples_bitfields(unfiltered_sample_ct, sample_exclude, &sample_exclude_ct, pheno_c, (filter_flags / FILTER_BINARY_CASES) & 1, pheno_nm);
     if (sample_exclude_ct == unfiltered_sample_ct) {
-      LOGPRINTF("Error: All %s removed due to case/control status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_CASES)? "cases" : "controls");
+      LOGERRPRINTF("Error: All %s removed due to case/control status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_CASES)? "cases" : "controls");
       goto plink1_dosage_ret_ALL_SAMPLES_EXCLUDED;
     }
     ii = sample_exclude_ct - ii;
@@ -954,7 +954,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     ii = sample_exclude_ct;
     filter_samples_bitfields(unfiltered_sample_ct, sample_exclude, &sample_exclude_ct, sex_male, (filter_flags / FILTER_BINARY_MALES) & 1, sex_nm);
     if (sample_exclude_ct == unfiltered_sample_ct) {
-      LOGPRINTF("Error: All %s removed due to gender filter (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_MALES)? "males" : "females");
+      LOGERRPRINTF("Error: All %s removed due to gender filter (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_MALES)? "males" : "females");
       goto plink1_dosage_ret_ALL_SAMPLES_EXCLUDED;
     }
     ii = sample_exclude_ct - ii;
@@ -964,7 +964,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     ii = sample_exclude_ct;
     filter_samples_bitfields(unfiltered_sample_ct, sample_exclude, &sample_exclude_ct, founder_info, (filter_flags / FILTER_BINARY_FOUNDERS) & 1, NULL);
     if (sample_exclude_ct == unfiltered_sample_ct) {
-      LOGPRINTF("Error: All %s removed due to founder status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_FOUNDERS)? "founders" : "nonfounders");
+      LOGERRPRINTF("Error: All %s removed due to founder status (--filter-%s).\n", g_species_plural, (filter_flags & FILTER_BINARY_FOUNDERS)? "founders" : "nonfounders");
       goto plink1_dosage_ret_ALL_SAMPLES_EXCLUDED;
     }
     ii = sample_exclude_ct - ii;
@@ -978,16 +978,30 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
   }
   sample_ct = unfiltered_sample_ct - sample_exclude_ct;
   if (!sample_ct) {
-    LOGPRINTF("Error: No %s pass QC.\n", g_species_plural);
+    LOGERRPRINTF("Error: No %s pass QC.\n", g_species_plural);
     goto plink1_dosage_ret_ALL_SAMPLES_EXCLUDED;
   }
   sample_cta4 = (sample_ct + 3) & (~3);
   sample_ctl = (sample_ct + (BITCT - 1)) / BITCT;
+  uii = do_glm && pheno_d;
   if (g_thread_ct > 1) {
-    logprint("Using 1 thread (no multithreaded calculations invoked).\n");
+    if (output_gz) {
+      LOGPRINTF("Using up to %u threads (change this with --threads).\n", g_thread_ct);
+    } else {
+      if (uii) {
+	logprint("Using 1 thread.\n");
+      } else {
+        logprint("Using 1 thread (no multithreaded calculations invoked).\n");
+      }
+    }
   } else {
     logprint("Using 1 thread.\n");
   }
+#ifndef NOLAPACK
+  if (uii && ((!known_procs) || (known_procs * 2 >= g_thread_ct))) {
+    logerrprint("Warning: This run includes BLAS/LAPACK linear algebra operations which\ncurrently disregard the --threads limit.  If this is problematic, you may want\nto recompile against single-threaded BLAS/LAPACK.\n");
+  }
+#endif
   if ((filter_flags & FILTER_MAKE_FOUNDERS) && (!(misc_flags & MISC_MAKE_FOUNDERS_FIRST))) {
     if (make_founders(unfiltered_sample_ct, sample_ct, sample_ids, max_sample_id_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, (misc_flags / MISC_MAKE_FOUNDERS_REQUIRE_2_MISSING) & 1, sample_exclude, founder_info)) {
       goto plink1_dosage_ret_NOMEM;
@@ -996,7 +1010,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
   if (covar_fname) {
     // update this as more covariate-referencing commands are added
     if (!do_glm) {
-      logprint("Warning: Ignoring --covar since no commands reference the covariates.\n");
+      logerrprint("Warning: Ignoring --covar since no commands reference the covariates.\n");
     } else {
       retval = load_covars(covar_fname, unfiltered_sample_ct, sample_exclude, sample_ct, sex_covar? sex_nm : NULL, sex_covar? sex_male : NULL, sample_ids, max_sample_id_len, missing_phenod, covar_modifier, covar_range_list_ptr, 0, &covar_ct, &covar_names, &max_covar_name_len, pheno_nm, &covar_nm, &covar_d, NULL, NULL);
       if (retval) {
@@ -1040,12 +1054,12 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 
   if (load_map) {
     if (unfiltered_marker_ct == marker_exclude_ct) {
-      logprint("Error: No variants remaining.\n");
+      logerrprint("Error: No variants remaining.\n");
       goto plink1_dosage_ret_ALL_MARKERS_EXCLUDED;
     }
     if (min_bp_space) {
       if (map_is_unsorted & UNSORTED_BP) {
-	logprint("Error: --bp-space requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
+	logerrprint("Error: --bp-space requires a sorted .bim file.  Retry this command after using\n--make-bed to sort your data.\n");
 	goto plink1_dosage_ret_INVALID_FORMAT;
       }
       enforce_min_bp_space(min_bp_space, unfiltered_marker_ct, marker_exclude, marker_pos, &marker_exclude_ct, chrom_info_ptr);
@@ -1061,7 +1075,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
   }
   if (!pheno_nm_ct) {
     if (do_glm) {
-      logprint("Error: No phenotypes present.\n");
+      logerrprint("Error: No phenotypes present.\n");
       goto plink1_dosage_ret_ALL_SAMPLES_EXCLUDED;
     }
     logprint("Note: No phenotypes present.\n");
@@ -1075,14 +1089,14 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     wordwrap(logbuf, 0);
     logprintb();
     if (standard_beta) {
-      logprint("Error: --dosage 'standard-beta' modifier cannot be used with a case/control\nphenotype.\n");
+      logerrprint("Error: --dosage 'standard-beta' modifier cannot be used with a case/control\nphenotype.\n");
       goto plink1_dosage_ret_INVALID_CMDLINE;
     }
   } else {
     logprint("Phenotype data is quantitative.\n");
 #ifdef NOLAPACK
     if (do_glm) {
-      logprint("Error: --dosage linear regression requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
+      logerrprint("Error: --dosage linear regression requires " PROG_NAME_CAPS " to be built with LAPACK.\n");
       goto plink1_dosage_ret_INVALID_CMDLINE;
     }
 #endif
@@ -1156,7 +1170,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     batch_ct = count_tokens(bufptr) - sepheader - 1; // underflow ok
     if (batch_ct) {
       if (batch_ct != 1) {
-        logprint("Error: Unexpected number of columns in --dosage list file.\n");
+        logerrprint("Error: Unexpected number of columns in --dosage list file.\n");
 	goto plink1_dosage_ret_INVALID_FORMAT;
       }
       batch_sizes = (uint32_t*)wkspace_base;
@@ -1362,7 +1376,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     if (infile_ct != 1) {
       for (batch_idx = 0; batch_idx < batch_ct; batch_idx++) {
 	if (batch_sizes[batch_idx] != 1) {
-	  logprint("Error: --dosage 'noheader' modifier cannot be used with multifile batches.\n");
+	  logerrprint("Error: --dosage 'noheader' modifier cannot be used with multifile batches.\n");
 	  goto plink1_dosage_ret_INVALID_CMDLINE;
 	}
       }
@@ -1376,10 +1390,10 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
   }
   if (freq_cc) {
     if (!do_glm) {
-      logprint("Error: --dosage 'case-control-freqs' modifier can only be used in association\nanalysis mode.\n");
+      logerrprint("Error: --dosage 'case-control-freqs' modifier can only be used in association\nanalysis mode.\n");
       goto plink1_dosage_ret_INVALID_CMDLINE;
     } else if (!pheno_c) {
-      logprint("Warning: '--dosage case-control-freqs' is silly with a quantitative phenotype.\n");
+      logerrprint("Warning: '--dosage case-control-freqs' is silly with a quantitative phenotype.\n");
     }
   }
   if (do_glm) {
@@ -1434,7 +1448,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
       dgels_lwork = -1;
       dgels_(&dgels_trans, &dgels_m, &dgels_n, &dgels_nrhs, dgels_a, &dgels_m, dgels_b, &dgels_ldb, &dxx, &dgels_lwork, &dgels_info);
       if (dxx > 2147483647.0) {
-	logprint("Error: Multiple linear regression problem too large for current LAPACK version.\n");
+	logerrprint("Error: Multiple linear regression problem too large for current LAPACK version.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto plink1_dosage_ret_1;
       }
@@ -1567,7 +1581,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 	  goto plink1_dosage_ret_OPEN_FAIL;
 	}
 	line_idx = 0;
-	uii = 1; // current skip value
+	uii = 1 + skip2; // current skip value
 	while (gzgets(gz_infiles[file_idx], tbuf, MAXLINELEN)) {
 	  line_idx++;
 	  if (!tbuf[MAXLINELEN - 1]) {
@@ -1615,7 +1629,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
       if (noheader) {
 	sample_uidx = 0;
 	next_unset_unsafe_ck(sample_exclude, &sample_uidx);
-	skip_vals[0] = 1 + format_val * sample_uidx;
+	skip_vals[0] = 1 + skip2 + format_val * sample_uidx;
 	uii = 1 + (format_val == 3);
 	for (read_idx = 1; read_idx < sample_ct; read_idx++) {
 	  prev_sample_uidx = sample_uidx++;
@@ -1661,7 +1675,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 	  sprintf(logbuf, "Error: Column %u of %s's header isn't 'A2'.\n", skip0 + skip1p1 + 2, &(fnames[(file_idx + file_idx_start) * max_fn_len]));
 	  goto plink1_dosage_ret_INVALID_FORMAT_WW;
 	}
-	uii = 1;
+	uii = 1 + skip2;
 	bufptr = skip_initial_spaces(token_endnn(bufptr2));
 	while (!is_eoln_kns(*bufptr)) {
           if (bsearch_read_fam_indiv(tbuf, sorted_sample_ids, max_sample_id_len, sample_ct, bufptr, &bufptr2, &ii)) {
@@ -1696,7 +1710,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     if ((batch_ct == 1) && (!noheader)) {
       ulii = sample_ct - popcount_longs(batch_samples, sample_ctl);
       if (ulii) {
-	LOGPRINTFWW("Warning: %" PRIuPTR " sample ID%s present in .fam file but missing from dosage file%s.\n", ulii, (ulii == 1)? "" : "s", (cur_batch_size == 1)? "" : "s");
+	LOGERRPRINTFWW("Warning: %" PRIuPTR " sample ID%s present in .fam file but missing from dosage file%s.\n", ulii, (ulii == 1)? "" : "s", (cur_batch_size == 1)? "" : "s");
       }
     }
 
@@ -1708,7 +1722,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 	do {
 	  if (!gzgets(gz_infiles[file_idx], loadbuf, loadbuf_size)) {
 	    if (file_idx) {
-	      logprint("Error: Misaligned dosage data files.\n");
+	      logerrprint("Error: Misaligned dosage data files.\n");
 	      goto plink1_dosage_ret_INVALID_FORMAT;
 	    }
 	    goto plink1_dosage_end_loop;
@@ -1896,6 +1910,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
 	  }
 	  sample_valid_ct_recip = 1.0 / ((double)((intptr_t)sample_valid_ct));
 	  dzz = dxx * sample_valid_ct_recip;
+	  // printf("sample_valid_ct: %lu  dzz: %g\n", sample_valid_ct, dzz);
 	  // dosageSSQ = sum(x^2 - bar{x}^2) = sum(x^2) - sum(x) * avg(x)
 	  // theoreticalVariance = avg(x) * (1 - avg(x))
 	  // empiricalVariance = 2 * (dosageSSQ / cnt)
@@ -2279,7 +2294,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
     break;
   plink1_dosage_ret_LONG_LINE:
     if (loadbuf_size == MAXLINEBUFLEN) {
-      LOGPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, &(fnames[(file_idx + file_idx_start) * max_fn_len]));
+      LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, &(fnames[(file_idx + file_idx_start) * max_fn_len]));
       retval = RET_INVALID_FORMAT;
       break;
     }
@@ -2290,7 +2305,7 @@ int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* out
   plink1_dosage_ret_INVALID_FORMAT_WW:
     wordwrap(logbuf, 0);
   plink1_dosage_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   plink1_dosage_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
diff --git a/plink_dosage.h b/plink_dosage.h
index 0c6b21b..7530915 100644
--- a/plink_dosage.h
+++ b/plink_dosage.h
@@ -31,6 +31,6 @@ void dosage_init(Dosage_info* doip);
 
 void dosage_cleanup(Dosage_info* doip);
 
-int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* outname, char* outname_end, char* phenoname, char* extractname, char* excludename, char* keepname, char* removename, char* keepfamname, char* removefamname, char* filtername, char* makepheno_str, char* phenoname_str, char* covar_fname, Two_col_params* qual_filter, Two_col_params* update_map, Two_col_params* update_name, char* update_ids_fname, char* update_parents_fname, char* update_sex_fname, char* filtervals_ [...]
+int32_t plink1_dosage(Dosage_info* doip, char* famname, char* mapname, char* outname, char* outname_end, char* phenoname, char* extractname, char* excludename, char* keepname, char* removename, char* keepfamname, char* removefamname, char* filtername, char* makepheno_str, char* phenoname_str, char* covar_fname, Two_col_params* qual_filter, Two_col_params* update_map, Two_col_params* update_name, char* update_ids_fname, char* update_parents_fname, char* update_sex_fname, char* filtervals_ [...]
 
 #endif
diff --git a/plink_family.c b/plink_family.c
index 91060b4..53806a2 100644
--- a/plink_family.c
+++ b/plink_family.c
@@ -437,7 +437,7 @@ int32_t get_trios_and_families(uintptr_t unfiltered_sample_ct, uintptr_t* sample
       }
     }
     if (remaining_edge_ct) {
-      logprint("Error: Pedigree graph is cyclic.  Check for evidence of time travel abuse in\nyour cohort.\n");
+      logerrprint("Error: Pedigree graph is cyclic.  Check for evidence of time travel abuse in\nyour cohort.\n");
       goto get_trios_and_families_ret_INVALID_FORMAT;
     }
     wkspace_reset(edge_list);
@@ -449,7 +449,7 @@ int32_t get_trios_and_families(uintptr_t unfiltered_sample_ct, uintptr_t* sample
     retval = RET_NOMEM;
     break;
   get_trios_and_families_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   get_trios_and_families_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -706,6 +706,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
   uint32_t include_duos = (fam_ip->mendel_modifier / MENDEL_DUOS) & 1;
   uint32_t multigen = (fam_ip->mendel_modifier / MENDEL_MULTIGEN) & 1;
   uint32_t var_first = fam_ip->mendel_modifier & MENDEL_FILTER_VAR_FIRST;
+  uint32_t full_error_list = calc_mendel && (!(fam_ip->mendel_modifier & MENDEL_SUMMARIES_ONLY));
   uint32_t varlen = 0;
   uint32_t chrom_name_len = 0;
   uint32_t new_marker_exclude_ct = 0;
@@ -759,7 +760,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
   uint32_t unn;
   marker_ct -= count_non_autosomal_markers(chrom_info_ptr, marker_exclude, 0, 1);
   if ((!marker_ct) || is_set(chrom_info_ptr->haploid_mask, 0)) {
-    logprint("Warning: Skipping --me/--mendel since there is no autosomal or Xchr data.\n");
+    logerrprint("Warning: Skipping --me/--mendel since there is no autosomal or Xchr data.\n");
     goto mendel_error_scan_ret_1;
   }
   retval = get_trios_and_families(unfiltered_sample_ct, sample_exclude, sample_ct, founder_info, sex_nm, sex_male, sample_ids, max_sample_id_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, &fids, &max_fid_len, &iids, &max_iid_len, &family_list, &family_ct, &trio_list, &trio_ct, &trio_lookup, include_duos, multigen);
@@ -767,7 +768,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
     goto mendel_error_scan_ret_1;
   }
   if (!trio_ct) {
-    LOGPRINTF("Warning: Skipping --me/--mendel since there are no %strios.\n", include_duos? "duos or " : "");
+    LOGERRPRINTF("Warning: Skipping --me/--mendel since there are no %strios.\n", include_duos? "duos or " : "");
     goto mendel_error_scan_ret_1;
   }
   trio_ct4 = (trio_ct + 3) / 4;
@@ -814,19 +815,21 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
     for (uii = 1; uii < 10; uii++) {
       errstrs[uii] = &(errstrs[0][uii * ulii]);
     }
-    if (multigen) {
+    if (multigen && full_error_list) {
       if (wkspace_alloc_ul_checked(&error_locs, trio_ctl * sizeof(intptr_t)) ||
-          wkspace_alloc_uc_checked(&cur_errors, trio_ct)) {
+	  wkspace_alloc_uc_checked(&cur_errors, trio_ct)) {
 	goto mendel_error_scan_ret_NOMEM;
       }
       fill_ulong_zero(error_locs, trio_ctl);
     }
-    memcpy(outname_end, ".mendel", 8);
-    if (fopen_checked(&outfile, outname, "w")) {
-      goto mendel_error_scan_ret_OPEN_FAIL;
+    if (full_error_list) {
+      memcpy(outname_end, ".mendel", 8);
+      if (fopen_checked(&outfile, outname, "w")) {
+	goto mendel_error_scan_ret_OPEN_FAIL;
+      }
+      sprintf(tbuf, "%%%us %%%us  CHR %%%us   CODE                 ERROR\n", plink_maxfid, plink_maxiid, plink_maxsnp);
+      fprintf(outfile, tbuf, "FID", "KID", "SNP");
     }
-    sprintf(tbuf, "%%%us %%%us  CHR %%%us   CODE                 ERROR\n", plink_maxfid, plink_maxiid, plink_maxsnp);
-    fprintf(outfile, tbuf, "FID", "KID", "SNP");
     memcpy(outname_end, ".lmendel", 9);
     if (fopen_checked(&outfile_l, outname, "w")) {
       goto mendel_error_scan_ret_OPEN_FAIL;
@@ -894,7 +897,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
 	  if (umm) {
 	    error_cts_tmp2[trio_idx] += umm & 0xffffff;
 	    cur_error_ct++;
-	    if (calc_mendel) {
+	    if (full_error_list) {
 	      umm >>= 24;
 	      wptr = fw_strcpy(plink_maxfid, &(fids[trio_idx * max_fid_len]), tbuf);
 	      *wptr++ = ' ';
@@ -934,7 +937,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
 	    if (umm) {
 	      error_cts_tmp2[trio_idx] += umm & 0xffffff;
 	      cur_error_ct++;
-	      if (calc_mendel) {
+	      if (full_error_list) {
 	        set_bit(error_locs, trio_idx);
 		umm >>= 24;
                 cur_errors[trio_idx] = (unsigned char)umm;
@@ -946,7 +949,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
 	    loadbuf[umm] |= (2 * ONELU) << ujj;
 	  }
 	}
-	if (calc_mendel && cur_error_ct) {
+	if (full_error_list && cur_error_ct) {
           trio_idx = 0;
 	  for (uii = 0; uii < cur_error_ct; trio_idx++, uii++) {
             next_set_ul_unsafe_ck(error_locs, &trio_idx);
@@ -1051,8 +1054,10 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
   }
   LOGPRINTF("--me/--mendel: %" PRIu64 " Mendel error%s detected.\n", tot_error_ct, (tot_error_ct == 1)? "" : "s");
   if (calc_mendel) {
-    if (fclose_null(&outfile)) {
-      goto mendel_error_scan_ret_WRITE_FAIL;
+    if (full_error_list) {
+      if (fclose_null(&outfile)) {
+	goto mendel_error_scan_ret_WRITE_FAIL;
+      }
     }
     if (fclose_null(&outfile_l)) {
       goto mendel_error_scan_ret_WRITE_FAIL;
@@ -1170,12 +1175,12 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
       }
     }
     *outname_end = '\0';
-    LOGPRINTFWW("Reports written to %s.mendel + %s.imendel + %s.fmendel + %s.lmendel .\n", outname, outname, outname, outname);
+    LOGPRINTFWW("Reports written to %s%s%s.imendel + %s.fmendel + %s.lmendel .\n", full_error_list? outname : "", full_error_list? ".mendel + " : "", outname, outname, outname);
   }
   if (fam_ip->mendel_modifier & MENDEL_FILTER) {
     *marker_exclude_ct_ptr += new_marker_exclude_ct;
     if (unfiltered_marker_ct == *marker_exclude_ct_ptr) {
-      logprint("Error: All variants excluded by --me.\n");
+      logerrprint("Error: All variants excluded by --me.\n");
       goto mendel_error_scan_ret_ALL_MARKERS_EXCLUDED;
     }
     if (var_first) {
@@ -1216,7 +1221,7 @@ int32_t mendel_error_scan(Family_info* fam_ip, FILE* bedfile, uintptr_t bed_offs
     }
     ulii = popcount_longs(sample_exclude, (unfiltered_sample_ct + (BITCT - 1)) / BITCT);
     if (unfiltered_sample_ct == ulii) {
-      LOGPRINTF("Error: All %s excluded by --me.\n", g_species_plural);
+      LOGERRPRINTF("Error: All %s excluded by --me.\n", g_species_plural);
       goto mendel_error_scan_ret_ALL_SAMPLES_EXCLUDED;
     }
     *sample_exclude_ct_ptr = ulii;
@@ -1715,7 +1720,7 @@ int32_t populate_pedigree_rel_info(Pedigree_rel_info* pri_ptr, uintptr_t unfilte
 	  }
 	}
 	if (sample_idx_write == remaining_sample_ct) {
-	  logprint("Error: Pedigree graph is cyclic.  Check for evidence of time travel abuse in\nyour cohort.\n");
+	  logerrprint("Error: Pedigree graph is cyclic.  Check for evidence of time travel abuse in\nyour cohort.\n");
 	  return RET_INVALID_FORMAT;
 	}
 	remaining_sample_ct = sample_idx_write;
@@ -2089,7 +2094,7 @@ int32_t tdt(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outna
   uint32_t umm;
   marker_ct -= count_non_autosomal_markers(chrom_info_ptr, marker_exclude, 0, 1);
   if ((!marker_ct) || is_set(chrom_info_ptr->haploid_mask, 0)) {
-    logprint("Warning: Skipping --tdt since there is no autosomal or Xchr data.\n");
+    logerrprint("Warning: Skipping --tdt since there is no autosomal or Xchr data.\n");
     goto tdt_ret_1;
   }
   retval = get_trios_and_families(unfiltered_sample_ct, sample_exclude, sample_ct, founder_info, sex_nm, sex_male, sample_ids, max_sample_id_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, &fids, &max_fid_len, &iids, &max_iid_len, &family_list, &family_ct, &trio_list, &trio_ct, &trio_error_lookup, 0, multigen);
@@ -2097,7 +2102,7 @@ int32_t tdt(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outna
     goto tdt_ret_1;
   }
   if (!trio_ct) {
-    logprint("Warning: Skipping --tdt since there are no trios.\n");
+    logerrprint("Warning: Skipping --tdt since there are no trios.\n");
     goto tdt_ret_1;
   }
   // now assemble list of nuclear families with at least one case child
@@ -2146,7 +2151,7 @@ int32_t tdt(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outna
 	    *lookup_ptr++ = ujj;
 	    *lookup_ptr++ = uii;
 	  }
-	  *lookup_ptr = 0x80000000;
+	  *lookup_ptr = 0x80000000U;
 	}
 	lookup_ptr++;
 	cur_child_ct = 0;
@@ -2174,7 +2179,7 @@ int32_t tdt(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outna
     family_ct++;
   }
   if (lookup_ptr == trio_nuclear_lookup) {
-    LOGPRINTF("Warning: Skipping --tdt%s since there are no trios with an affected child%s.\n", poo_test? " poo" : "", poo_test? "" : ", and no\ndiscordant parent pairs");
+    LOGERRPRINTF("Warning: Skipping --tdt%s since there are no trios with an affected child%s.\n", poo_test? " poo" : "", poo_test? "" : ", and no\ndiscordant parent pairs");
     goto tdt_ret_1;
   }
   wkspace_shrink_top(trio_nuclear_lookup, ((uintptr_t)(lookup_ptr - trio_nuclear_lookup)) * sizeof(int32_t));
@@ -2195,7 +2200,7 @@ int32_t tdt(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outna
     goto tdt_ret_NOMEM;
   }
   if (fam_ip->tdt_modifier & (TDT_PERM | TDT_MPERM)) {
-    logprint("Error: --tdt permutation tests are currently under development.\n");
+    logerrprint("Error: --tdt permutation tests are currently under development.\n");
     retval = RET_CALC_NOT_YET_SUPPORTED;
     goto tdt_ret_1;
   }
@@ -2531,7 +2536,7 @@ int32_t tdt(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outna
   return retval;
 }
 
-int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclude, uintptr_t sample_ct, uintptr_t* pheno_nm, uintptr_t* founder_info, char* sample_ids, uintptr_t max_sample_id_len, uintptr_t max_fid_len, char* paternal_ids, uintptr_t max_paternal_id_len, char* maternal_ids, uintptr_t max_maternal_id_len, uint64_t* family_list, uint64_t* trio_list, uint32_t family_ct, uintptr_t trio_ct, uint32_t test_type, uintptr_t** lm_eligible_ptr, uintptr_t** lm_within2_founder_ptr, u [...]
+int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclude, uintptr_t sample_ct, uintptr_t* pheno_nm, uintptr_t* founder_info, char* sample_ids, uintptr_t max_sample_id_len, uintptr_t max_fid_len, char* paternal_ids, uintptr_t max_paternal_id_len, char* maternal_ids, uintptr_t max_maternal_id_len, uint64_t* family_list, uint64_t* trio_list, uint32_t family_ct, uintptr_t trio_ct, uint32_t test_type, uintptr_t** size_one_sibships_ptr, uintptr_t** lm_eligible_ptr, ui [...]
   // On top of get_trios_and_families()'s return values, we need the following
   // information for the main dfam() and qfam() loops:
   // 1. sample idx -> family/sibship idx array
@@ -2542,6 +2547,10 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
   // There is also some qfam-specific initialization here (e.g. a divorcee with
   // children from two different spouses may be excluded from the linear
   // model).  test_type is zero for dfam and nonzero for qfam.
+
+  // It's probably appropriate to split this into two functions in the future,
+  // one for dfam and one for qfam; the differences make this difficult to
+  // maintain.
   uintptr_t unfiltered_sample_ctl = (unfiltered_sample_ct + (BITCT - 1)) / BITCT;
   uintptr_t sample_ctl = (sample_ct + (BITCT - 1)) / BITCT;
   uintptr_t max_merged_id_len = max_fid_len + max_paternal_id_len + max_maternal_id_len + sizeof(int32_t);
@@ -2567,6 +2576,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
   uint32_t* fs_starts;
   uint32_t* fss_contents;
   uintptr_t topsize_bak;
+  uintptr_t topsize_bak2;
   uintptr_t cur_sample_ct;
   uintptr_t sample_uidx;
   uintptr_t sample_idx;
@@ -2615,7 +2625,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
   // Temporary bitfields used to track which parents are (i) part of multiple
   // families, and (ii) not a child in any of them.  To ensure results are not
   // dependent on the order of samples in the dataset, we now exclude these
-  // parents from the permutation.  (todo: compute an average in this case
+  // parents from the QFAM permutation.  (todo: compute an average in this case
   // instead?)
   sample_uidx_to_idx = (uint32_t*)top_alloc(&topsize, unfiltered_sample_ct * sizeof(int32_t));
   if (!sample_uidx_to_idx) {
@@ -2625,7 +2635,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
   if (!ulptr) {
     goto get_sibship_info_ret_NOMEM2;
   }
-  ulii = topsize;
+  topsize_bak2 = topsize;
   ulptr2 = (uintptr_t*)top_alloc(&topsize, unfiltered_sample_ctl * sizeof(intptr_t));
   if (!ulptr2) {
     goto get_sibship_info_ret_NOMEM2;
@@ -2644,9 +2654,10 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
   fill_ulong_zero(ulptr, unfiltered_sample_ctl); // is a double-parent
   fill_ulong_zero(ulptr2, unfiltered_sample_ctl); // is a child
   if (family_ct) {
+    // iterate over all parents
     while (1) {
       ullii = family_list[family_idx];
-      // uii, ukk = unfiltered idxs of parents
+      // uii, ukk = unfiltered idxs of father, mother respectively
       // ujj, umm = filtered idxs
       uii = (uint32_t)ullii;
       ujj = sample_uidx_to_idx[uii];
@@ -2661,6 +2672,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
       }
       if (is_set(not_in_family, uii)) {
 	if (sample_to_fss_idx[ujj] == 0xffffffffU) {
+	  // missing father
 	  sample_to_fss_idx[ujj] = family_idx;
 	}
 	clear_bit(not_in_family, uii);
@@ -2670,6 +2682,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
       fss_contents[fssc_idx++] = umm;
       if (is_set(not_in_family, ukk)) {
 	if (sample_to_fss_idx[umm] == 0xffffffffU) {
+	  // missing mother
 	  sample_to_fss_idx[umm] = family_idx;
 	}
 	clear_bit(not_in_family, ukk);
@@ -2678,6 +2691,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
       }
 
       ullii = trio_list[trio_idx];
+      // do-while since there's at least one trio for each set of parents
       do {
 	uii = (uint32_t)ullii;
 	ujj = sample_uidx_to_idx[uii];
@@ -2695,7 +2709,16 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
  get_sibship_info_first_pass_done:
   bitfield_andnot(not_in_family, ulptr2, unfiltered_sample_ctl);
   wkspace_shrink_top(fss_contents, (fssc_idx + popcount_longs(not_in_family, unfiltered_sample_ctl)) * sizeof(int32_t));
-  bitfield_andnot(ulptr, ulptr2, unfiltered_sample_ctl);
+  if (test_type) {
+    bitfield_andnot(ulptr, ulptr2, unfiltered_sample_ctl);
+  } else {
+    bitfield_exclude_to_include(ulptr2, ulptr, unfiltered_sample_ct);
+  }
+  // qfam: ulptr = double-parents who aren't also a child of two parents in
+  //               immediate dataset
+  // dfam: ulptr = everyone who isn't a child of two parents in immediate
+  //               dataset
+
   if (is_within2) {
     bitfield_andnot(tmp_within2_founder, ulptr, unfiltered_sample_ctl);
     bitfield_and(tmp_within2_founder, founder_info, unfiltered_sample_ctl);
@@ -2703,17 +2726,24 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
     // families, and (ii) have a different phenotype from their partner.
     collapse_copy_bitarr(unfiltered_sample_ct, tmp_within2_founder, sample_exclude, sample_ct, lm_within2_founder);
   }
-  bitfield_andnot_reversed_args(ulptr, pheno_nm, unfiltered_sample_ctl);
   if (test_type) {
+    bitfield_andnot_reversed_args(ulptr, pheno_nm, unfiltered_sample_ctl);
     if (test_type == QFAM_WITHIN1) {
       bitfield_andnot(ulptr, founder_info, unfiltered_sample_ctl);
     }
     collapse_copy_bitarr(unfiltered_sample_ct, ulptr, sample_exclude, sample_ct, lm_eligible);
+    bitfield_andnot_copy(unfiltered_sample_ctl, ulptr, not_in_family, founder_info);
+  } else {
+    bitfield_and(ulptr, pheno_nm, unfiltered_sample_ctl);
+    bitfield_andnot(ulptr, founder_info, unfiltered_sample_ctl);
   }
-  topsize = ulii;
+  topsize = topsize_bak2;
+
+  // qfam: not a parent or child in a trio, not a founder
+  // dfam: not a child in a trio, not a founder; parent ok
 
-  bitfield_andnot_copy(unfiltered_sample_ctl, ulptr, not_in_family, founder_info);
   cur_sample_ct = popcount_longs(ulptr, unfiltered_sample_ctl);
+
   wkspace_left -= topsize;
   if (wkspace_alloc_ui_checked(&fs_starts, (1 + family_ct + (cur_sample_ct / 2)) * sizeof(int32_t))) {
     goto get_sibship_info_ret_NOMEM2;
@@ -2734,7 +2764,7 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
     family_idx++;
   }
   if (cur_sample_ct > 1) {
-    // identify sibships of size >1
+    // identify size-2+ sibships
     ulii = topsize;
     merged_ids = (char*)top_alloc(&topsize, max_merged_id_len * cur_sample_ct);
     if (!merged_ids) {
@@ -2809,6 +2839,14 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
     *sample_lm_to_fss_idx_ptr = sample_lm_to_fss_idx;
     *lm_ct_ptr = ulii;
   } else {
+    // bugfix: for DFAM, we also need to prevent size-1 sibships from being
+    // included in the unrelated cluster
+    if (wkspace_alloc_ul_checked(size_one_sibships_ptr, unfiltered_sample_ctl * sizeof(intptr_t))) {
+      goto get_sibship_info_ret_NOMEM2;
+    }
+    memcpy(*size_one_sibships_ptr, not_in_family, unfiltered_sample_ctl * sizeof(intptr_t));
+    bitfield_and(*size_one_sibships_ptr, ulptr, unfiltered_sample_ctl);
+
     // return sample_to_fss_idx in place of sample_lm_to_fss_idx
     *sample_lm_to_fss_idx_ptr = sample_to_fss_idx;
   }
@@ -2827,12 +2865,13 @@ int32_t get_sibship_info(uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclu
 }
 
 // multithread globals
-/*
 static double* g_maxt_extreme_stat;
-static double* g_maxt_thread_results;
+// static double* g_maxt_thread_results;
 static double* g_mperm_save_all;
 static uintptr_t* g_pheno_c;
-*/
+// static uintptr_t* g_dfam_flipa;
+// static uintptr_t* g_dfam_perm_vecs;
+// static uintptr_t* g_dfam_perm_vecst;
 
 static uintptr_t* g_loadbuf;
 static uintptr_t* g_lm_eligible;
@@ -2873,6 +2912,7 @@ static double g_adaptive_slope;
 static double g_aperm_alpha;
 static double g_adaptive_ci_zt;
 
+// note that 4 is encoded as 0 since they're treated the same.
 // tried encoding this in a single 32-bit integer, but that appears to be
 // slower.
 const uint8_t dfam_allele_ct_table[] =
@@ -2915,9 +2955,6 @@ void dfam_sibship_calc(uint32_t cur_case_ct, uint32_t case_hom_a1_ct, uint32_t c
 }
 
 int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outname, char* outname_end, double pfilter, double output_min_p, uint32_t mtest_adjust, double adjust_lambda, uintptr_t unfiltered_marker_ct, uintptr_t* marker_exclude_orig, uintptr_t marker_ct_orig, char* marker_ids, uintptr_t max_marker_id_len, uint32_t plink_maxsnp, char** marker_allele_ptrs, uintptr_t max_marker_allele_len, uintptr_t* marker_reverse, uintptr_t unfiltered_sample_ct, uintptr_t* sample_exclude,  [...]
-  logprint("Error: --dfam is currently under development.\n");
-  return RET_CALC_NOT_YET_SUPPORTED;
-  /*
   unsigned char* wkspace_mark = wkspace_base;
   FILE* outfile = NULL;
   FILE* outfile_msa = NULL;
@@ -2939,7 +2976,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   uint32_t perm_maxt_nst = (fam_ip->dfam_modifier & DFAM_MPERM) && (!is_set_test);
   uint32_t do_perms = fam_ip->dfam_modifier & (DFAM_PERM | DFAM_MPERM);
   uint32_t do_perms_nst = do_perms && (!is_set_test);
-  uint32_t perm_count = fam_ip->dfam_modifier & DFAM_PERM_COUNT;
+  // uint32_t perm_count = fam_ip->dfam_modifier & DFAM_PERM_COUNT;
   uint32_t fill_orig_chisq = do_perms || mtest_adjust;
   uint32_t no_unrelateds = (fam_ip->dfam_modifier & DFAM_NO_UNRELATEDS) || (within_cmdflag && (!cluster_ct));
   uint32_t family_all_case_children_ct = 0;
@@ -2947,7 +2984,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   uint32_t sibship_mixed_ct = 0;
   uint32_t unrelated_cluster_ct = 0;
   uint32_t pct = 0;
-  uint32_t max_thread_ct = g_thread_ct;
+  // uint32_t max_thread_ct = g_thread_ct;
   uint32_t perm_pass_idx = 0;
   uint32_t perms_total = 0;
   uint32_t perms_done = 0;
@@ -2959,10 +2996,10 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   uintptr_t* workbuf;
   uintptr_t* marker_exclude;
   uintptr_t* dfam_sample_exclude;
+  uintptr_t* size_one_sibships;
   double* maxt_extreme_stat = NULL;
   uint32_t mu_table[MODEL_BLOCKSIZE];
-  char* outname_end2;
-  char* wptr_start;
+  // char* outname_end2;
   char* wptr;
   uint64_t* family_list;
   uint64_t* trio_list;
@@ -2989,6 +3026,8 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   double case_proportion;
   double case_expected_a1_ct;
   double case_var_a1_ct;
+  double chisq;
+  double pval;
   double dxx;
   uint32_t family_ct;
   uint32_t fs_ct;
@@ -3006,7 +3045,6 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   uint32_t dfam_sample_ctl2;
   uint32_t chrom_fo_idx;
   uint32_t chrom_idx;
-  uint32_t chrom_end;
   uint32_t block_size;
   uint32_t block_end;
   uint32_t marker_bidx;
@@ -3039,7 +3077,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   if (uii) {
     LOGPRINTF("Excluding %u X/MT/haploid variant%s from DFAM test.\n", uii, (uii == 1)? "" : "s");
     if (uii == marker_ct_orig_autosomal) {
-      logprint("Error: No variants remaining for DFAM analysis.\n");
+      logerrprint("Error: No variants remaining for DFAM analysis.\n");
       goto dfam_ret_INVALID_CMDLINE;
     }
     marker_ct_orig_autosomal -= uii;
@@ -3055,12 +3093,12 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
       }
     }
   } else if (is_set(chrom_info_ptr->haploid_mask, 0)) {
-    logprint("Error: DFAM test does not support haploid data.\n");
+    logerrprint("Error: DFAM test does not support haploid data.\n");
     goto dfam_ret_INVALID_CMDLINE;
   }
   uii = popcount_longs_exclude(pheno_c, sample_exclude, unfiltered_sample_ct);
   if (!uii) {
-    logprint("Error: DFAM test requires at least one case.\n");
+    logerrprint("Error: DFAM test requires at least one case.\n");
     goto dfam_ret_INVALID_CMDLINE;
   }
   marker_exclude = marker_exclude_orig_autosomal;
@@ -3089,11 +3127,11 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   }
 #ifdef __LP64__
   if ((12 * sample_ct + 2 * family_ct) > 0xffffffffLLU) {
-    logprint("Error: Too many samples and families for DFAM test.\n");
+    logerrprint("Error: Too many samples and families for DFAM test.\n");
     goto dfam_ret_INVALID_CMDLINE;
   }
 #endif
-  if (get_sibship_info(unfiltered_sample_ct, sample_exclude, sample_ct, pheno_nm, founder_info, sample_ids, max_sample_id_len, max_fid_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, family_list, trio_list, family_ct, trio_ct, 0, NULL, NULL, &fs_starts, &fss_contents, &sample_to_fss_idx, &fs_ct, NULL, NULL)) {
+  if (get_sibship_info(unfiltered_sample_ct, sample_exclude, sample_ct, pheno_nm, founder_info, sample_ids, max_sample_id_len, max_fid_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, family_list, trio_list, family_ct, trio_ct, 0, &size_one_sibships, NULL, NULL, &fs_starts, &fss_contents, &sample_to_fss_idx, &fs_ct, NULL, NULL)) {
     goto dfam_ret_NOMEM;
   }
   // Prepare final family, sibship, and unrelated cluster data structures.
@@ -3204,19 +3242,20 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
       goto dfam_ret_NOMEM;
     }
     // --within on an empty file actually causes --dfam to behave differently
-    // than no --within at all in PLINK 1.07.  Replicate this for now.
+    // (no unrelated cluster at all) than no --within at all (one big unrelated
+    // cluster) in PLINK 1.07.  Replicate this for now.
     if (within_cmdflag) {
       if (fill_sample_to_cluster(unfiltered_sample_ct, sample_exclude, sample_ct, cluster_ct, cluster_map, cluster_starts, sample_to_cluster, NULL)) {
 	goto dfam_ret_NOMEM;
       }
     } else {
-      // Start everyone in the same cluster.
       fill_uint_zero(sample_to_cluster, sample_ct);
       cluster_ct = 1;
     }
     for (sample_idx = 0; sample_idx < sample_ct; sample_idx++) {
-      // Remove families and sibships.
-      if (sample_to_fss_idx[sample_idx] != 0xffffffffU) {
+      // Remove families and size-2+ sibships.
+      // bugfix: also remove size-1 sibships.
+      if ((sample_to_fss_idx[sample_idx] != 0xffffffffU) || IS_SET(size_one_sibships, idx_to_uidx[sample_idx])) {
 	sample_to_cluster[sample_idx] = 0xffffffffU;
       }
     }
@@ -3263,15 +3302,10 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   wkspace_reset((unsigned char*)idx_to_uidx);
   wkspace_shrink_top(dfam_iteration_order, (cur_dfam_ptr - dfam_iteration_order) * sizeof(int32_t));
   if (do_perms) {
-    logprint("Error: --dfam permutation tests are currently under development.\n");
+    logerrprint("Error: --dfam permutation tests are currently under development.\n");
     retval = RET_CALC_NOT_YET_SUPPORTED;
     goto dfam_ret_1;
   }
-  if (mtest_adjust || do_perms) {
-    if (wkspace_alloc_d_checked(&orig_chisq, marker_ct * sizeof(double))) {
-      goto dfam_ret_NOMEM;
-    }
-  }
   dfam_sample_ct = unfiltered_sample_ct - popcount_longs(dfam_sample_exclude, unfiltered_sample_ctl);
   dfam_sample_ctl2 = (dfam_sample_ct + (BITCT2 - 1)) / BITCT2;
   if (wkspace_alloc_ui_checked(&uidx_to_idx, unfiltered_sample_ct * sizeof(int32_t))) {
@@ -3300,7 +3334,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
     }
   }
   // DEBUG
-  printf("%u %u %u %u\n", family_all_case_children_ct, family_mixed_ct, sibship_mixed_ct, unrelated_cluster_ct);
+  // printf("*** %u %u %u %u\n", family_all_case_children_ct, family_mixed_ct, sibship_mixed_ct, unrelated_cluster_ct);
   wkspace_reset((unsigned char*)uidx_to_idx);
   if (wkspace_alloc_ul_checked(&dfam_pheno_c, dfam_sample_ctl2 * sizeof(intptr_t)) ||
       wkspace_alloc_ul_checked(&loadbuf_raw, unfiltered_sample_ctl2 * sizeof(intptr_t)) ||
@@ -3319,9 +3353,10 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   // no X/haploid/MT, so no haploid filters
 
   if (fill_orig_chisq) {
-    if (wkspace_alloc_d_checked(&g_orig_stat, marker_ct * sizeof(double))) {
+    if (wkspace_alloc_d_checked(&orig_chisq, marker_ct * sizeof(double))) {
       goto dfam_ret_NOMEM;
     }
+    g_orig_stat = orig_chisq;
   }
 
   ulii = 2 * max_marker_allele_len + plink_maxsnp + MAX_ID_LEN + 256;
@@ -3333,6 +3368,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
 
   // permutation test boilerplate mostly copied from qassoc() in plink_assoc.c,
   // since it's also restricted to autosomes
+  g_perms_done = 0;
   g_mperm_save_all = NULL;
   if (perm_maxt_nst) {
     perms_total = fam_ip->dfam_mperm_val;
@@ -3376,7 +3412,8 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
     }
   }
 
-  outname_end2 = memcpyb(outname_end, ".dfam", 6);
+  memcpy(outname_end, ".dfam", 6);
+  // outname_end2 = memcpyb(outname_end, ".dfam", 6);
   if (fopen_checked(&outfile, outname, "w")) {
     goto dfam_ret_OPEN_FAIL;
   }
@@ -3402,11 +3439,28 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   fputs("0%", stdout);
   fflush(stdout);
   // ----- begin main loop -----
- dfam_more_perms:
+  // dfam_more_perms:
   if (do_perms_nst) {
-    if (!perm_pass_idx) {
-      // ...
+    logerrprint("Error: --dfam permutation tests are currently under development.\n");
+    retval = RET_CALC_NOT_YET_SUPPORTED;
+    goto dfam_ret_1;
+    /*
+    if (perm_adapt_nst && perm_pass_idx) {
+      while (g_first_adapt_check <= g_perms_done) {
+	// APERM_MAX prevents infinite loop here
+	g_first_adapt_check += (int32_t)(apip->init_interval + ((int32_t)g_first_adapt_check) * apip->interval_slope);
+      }
+    }
+    // g_perm_vec_ct memory allocation dependencies:
+    //   ;;;
+    g_perm_vec_ct = perm_batch_size;
+    if (g_perm_vec_ct > perms_total - g_perms_done) {
+      g_perm_vec_ct = perms_total - g_perms_done;
+    }
+    if (wkspace_alloc_ul_checked(&g_dfam_perm_vecs, g_perm_vec_ct * sample_ctv2 * sizeof(intptr_t))) {
+      goto dfam_ret_NOMEM;
     }
+    */
   }
   chrom_fo_idx = 0xffffffffU;
   marker_uidx = next_unset_unsafe(marker_exclude, 0);
@@ -3415,7 +3469,6 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   }
   marker_idx = 0;
   marker_idx2 = 0;
-  chrom_end = 0;
   do {
     // since X/haploid/MT is not supported, ignore chromosome boundaries in
     // this loop
@@ -3478,9 +3531,9 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
       //      quad_denom           += # of het parents
       //      total_count          += [A1 allele count among case kids]
       //      twice_total_expected += [# of case kids] * [parental A1 ct]
-      // 3. Iterate through sibships.  If all case siblings, or all control
-      //    siblings, have missing genotypes, skip.  Otherwise (see lines
-      //    420-456 of PLINK 1.07 dfam.cpp),
+      // 3. Iterate through mixed sibships.  If all case siblings, or all
+      //    control siblings, have missing genotypes, skip.  Otherwise (see
+      //    lines 420-456 of PLINK 1.07 dfam.cpp),
       //      case_expected_hom_a1 := [case sib ct] * [sib hom A1 ct] /
       //                              [sib ct]
       //      case_expected_het    := [case sib ct] * [sib het ct] / [sib ct]
@@ -3512,7 +3565,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
       //      case_expected_a1_ct := [case ct] * [A1 ct] / [cluster size]
       //      case_var_a1_ct      := ([case ct] * [ctrl ct]
       //                              [A1 ct] * [A2 ct]) /
-      //                             (([clst size]^2) * ([clst size] - 1))
+      //                             (([clst size]^2) * (2 * [clst size] - 1))
       //      numer          += case_a1_ct - case_expected_a1_ct
       //      denom          += case_var_a1_ct
       //      total_count    += case_a1_ct
@@ -3553,7 +3606,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
 	  }
 	  if (cur_case_ct) {
 	    twice_numer += (int32_t)(2 * case_a1_ct) - (int32_t)(cur_case_ct * parental_a1_ct);
-	    quad_denom += 2 - (parental_a1_ct & 1);
+	    quad_denom += (2 - (parental_a1_ct & 1)) * cur_case_ct;
 	    total_count += case_a1_ct;
 	    twice_total_expected += cur_case_ct * parental_a1_ct;
 	  }
@@ -3605,7 +3658,7 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
 	  } else {
 	    case_a1_ct = 2 * case_hom_a1_ct + case_het_ct;
 	    twice_numer += (int32_t)(2 * case_a1_ct) - (int32_t)(cur_case_ct * parental_a1_ct);
-	    quad_denom += 2 - (parental_a1_ct & 1);
+	    quad_denom += (2 - (parental_a1_ct & 1)) * (cur_case_ct + cur_ctrl_ct);
 	    total_count += case_a1_ct;
 	    twice_total_expected += cur_case_ct * parental_a1_ct;
 	  }
@@ -3702,14 +3755,37 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
 	  case_proportion = ((double)((int32_t)cur_case_ct)) / dxx;
 	  ujj = 2 * hom_a1_ct + het_ct;
 	  case_expected_a1_ct = case_proportion * ((double)((int32_t)ujj));
-	  case_var_a1_ct = case_expected_a1_ct * cur_ctrl_ct * ((double)((int32_t)(2 * uii - ujj))) / (dxx * (dxx - 1));
+	  case_var_a1_ct = case_expected_a1_ct * ((double)((int32_t)(2 * uii - ujj))) * ((double)((int32_t)cur_ctrl_ct)) / (dxx * (2 * dxx - 1));
           numer += case_a1_ct - case_expected_a1_ct;
 	  denom += case_var_a1_ct;
 	  total_expected += case_expected_a1_ct;
 	}
-	printf("%g %g %u %g\n", numer, denom, total_count, total_expected);
-	if (marker_bidx == 2) {
-	  exit(1);
+	chisq = numer * numer / denom;
+	if (fill_orig_chisq) {
+	  orig_chisq[marker_idx + marker_bidx] = chisq;
+	}
+	pval = chiprob_p(chisq, 1);
+	if ((pfilter == 2.0) || ((pval <= pfilter) && (pval >= 0.0))) {
+	  wptr = width_force(4, textbuf, chrom_name_write(textbuf, chrom_info_ptr, get_marker_chrom(chrom_info_ptr, marker_uidx2)));
+	  *wptr++ = ' ';
+	  wptr = fw_strcpy(plink_maxsnp, &(marker_ids[marker_uidx2 * max_marker_id_len]), wptr);
+	  *wptr++ = ' ';
+	  wptr = fw_strcpy(4, marker_allele_ptrs[2 * marker_uidx2], wptr);
+	  *wptr++ = ' ';
+	  wptr = fw_strcpy(4, marker_allele_ptrs[2 * marker_uidx2 + 1], wptr);
+	  *wptr++ = ' ';
+	  wptr = uint32_writew8x(wptr, total_count, ' ');
+	  wptr = double_g_writewx4x(wptr, total_expected, 8, ' ');
+	  if (denom != 0.0) {
+	    wptr = double_g_writewx4x(wptr, chisq, 12, ' ');
+	    wptr = double_g_writewx4(wptr, pval, 12);
+	  } else {
+	    wptr = memcpya(wptr, "          NA           NA", 25);
+	  }
+	  wptr = memcpya(wptr, " \n", 2);
+	  if (fwrite_checked(textbuf, wptr - textbuf, outfile)) {
+	    goto dfam_ret_WRITE_FAIL;
+	  }
 	}
       }
     }
@@ -3739,15 +3815,17 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
       goto dfam_ret_WRITE_FAIL;
     }
     if (!is_set_test) {
-      if (wkspace_alloc_ui_checked(&idx_to_uidx, marker_ct * sizeof(int32_t))) {
-	goto dfam_ret_NOMEM;
-      }
-      fill_idx_to_uidx(marker_exclude, unfiltered_marker_ct, marker_ct, idx_to_uidx);
-      retval = multcomp(outname, outname_end, idx_to_uidx, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, chrom_info_ptr, orig_chisq, pfilter, output_min_p, mtest_adjust, 0, adjust_lambda, NULL, NULL);
-      if (retval) {
-	goto dfam_ret_1;
+      if (mtest_adjust) {
+	if (wkspace_alloc_ui_checked(&idx_to_uidx, marker_ct * sizeof(int32_t))) {
+	  goto dfam_ret_NOMEM;
+	}
+	fill_idx_to_uidx(marker_exclude, unfiltered_marker_ct, marker_ct, idx_to_uidx);
+	retval = multcomp(outname, outname_end, idx_to_uidx, marker_ct, marker_ids, max_marker_id_len, plink_maxsnp, chrom_info_ptr, orig_chisq, pfilter, output_min_p, mtest_adjust, 0, adjust_lambda, NULL, NULL);
+	if (retval) {
+	  goto dfam_ret_1;
+	}
+	wkspace_reset(idx_to_uidx);
       }
-      wkspace_reset(idx_to_uidx);
       // if (mperm_save & MPERM_DUMP_ALL) { ...
     } else {
       // retval = dfam_set_test(threads, bedfile, bed_offset, outname, outname_end, ...);
@@ -3786,7 +3864,6 @@ int32_t dfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   fclose_cond(outfile);
   fclose_cond(outfile_msa);
   return retval;
-  */
 }
 
 void uint32_permute(uint32_t* perm_arr, uint32_t* precomputed_mods, sfmt_t* sfmtp, uint32_t ct) {
@@ -3802,9 +3879,9 @@ void uint32_permute(uint32_t* perm_arr, uint32_t* precomputed_mods, sfmt_t* sfmt
     do {
       urand = sfmt_genrand_uint32(sfmtp);
     } while (urand < lbound);
-    // integer modulus is slow.  some of the other permutation generator handle
-    // many at once, in a manner that allows magic number divide to be employed
-    // efficiently.
+    // integer modulus is slow.  some of the other permutation generators
+    // handle many at once, in a manner that allows magic number divide to be
+    // employed efficiently.
     urand %= write_idx + 1;
     perm_arr[write_idx] = perm_arr[urand];
     perm_arr[urand] = write_idx;
@@ -4338,12 +4415,12 @@ int32_t qfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   if (uii) {
     LOGPRINTF("Excluding %u X/MT/haploid variant%s from QFAM test.\n", uii, (uii == 1)? "" : "s");
     if (uii == marker_ct) {
-      logprint("Error: No variants remaining for QFAM analysis.\n");
+      logerrprint("Error: No variants remaining for QFAM analysis.\n");
       goto qfam_ret_INVALID_CMDLINE;
     }
     marker_ct -= uii;
   } else if (is_set(chrom_info_ptr->haploid_mask, 0)) {
-    logprint("Error: QFAM test does not currently support haploid data.\n");
+    logerrprint("Error: QFAM test does not currently support haploid data.\n");
     goto qfam_ret_INVALID_CMDLINE;
   }
   // no --mendel-duos support for now
@@ -4358,19 +4435,19 @@ int32_t qfam(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* outn
   // (okay, no need to check anyway, but best to document this overflow
   // possibility.)
   if ((sample_ct + 2 * family_ct) > 0xffffffffLLU) {
-    logprint("Error: Too many samples and families for QFAM test.\n");
+    logerrprint("Error: Too many samples and families for QFAM test.\n");
     goto qfam_ret_INVALID_CMDLINE;
   }
 #endif
-  if (get_sibship_info(unfiltered_sample_ct, sample_exclude, sample_ct, pheno_nm, founder_info, sample_ids, max_sample_id_len, max_fid_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, family_list, trio_list, family_ct, trio_ct, test_type, &lm_eligible, &lm_within2_founder, &fs_starts, &fss_contents, &sample_lm_to_fss_idx, &fs_ct, &lm_ct, &singleton_ct)) {
+  if (get_sibship_info(unfiltered_sample_ct, sample_exclude, sample_ct, pheno_nm, founder_info, sample_ids, max_sample_id_len, max_fid_len, paternal_ids, max_paternal_id_len, maternal_ids, max_maternal_id_len, family_list, trio_list, family_ct, trio_ct, test_type, NULL, &lm_eligible, &lm_within2_founder, &fs_starts, &fss_contents, &sample_lm_to_fss_idx, &fs_ct, &lm_ct, &singleton_ct)) {
     goto qfam_ret_NOMEM;
   }
   fss_ct = fs_ct + singleton_ct;
   if (fss_ct < 2) {
-    logprint("Error: QFAM test requires at least two families.\n");
+    logerrprint("Error: QFAM test requires at least two families.\n");
     goto qfam_ret_INVALID_CMDLINE;
   } else if (lm_ct < 3) {
-    LOGPRINTF("Error: Less than three eligible %ss for QFAM test.\n", (test_type == QFAM_WITHIN1)? "nonfounder" : "sample");
+    LOGERRPRINTF("Error: Less than three eligible %ss for QFAM test.\n", (test_type == QFAM_WITHIN1)? "nonfounder" : "sample");
     goto qfam_ret_INVALID_CMDLINE;
   }
   g_fs_starts = fs_starts;
diff --git a/plink_filter.c b/plink_filter.c
index 793a155..8b66155 100644
--- a/plink_filter.c
+++ b/plink_filter.c
@@ -144,14 +144,14 @@ int32_t keep_or_remove(char* fname, char* sorted_ids, uintptr_t sorted_ids_ct, u
   memcpy(exclude_arr, exclude_arr_new, unfiltered_ctl * sizeof(intptr_t));
   *exclude_ct_ptr = popcount_longs(exclude_arr, unfiltered_ctl);
   if (*exclude_ct_ptr == unfiltered_ct) {
-    LOGPRINTF("Error: No %s remaining after --%s.\n", g_species_plural, keep_or_remove_flag_str(flags));
+    LOGERRPRINTF("Error: No %s remaining after --%s.\n", g_species_plural, keep_or_remove_flag_str(flags));
     goto keep_or_remove_ret_ALL_SAMPLES_EXCLUDED;
   }
   unfiltered_ct -= *exclude_ct_ptr; // now filtered count
   LOGPRINTF("--%s: %" PRIuPTR " %s remaining.\n", keep_or_remove_flag_str(flags), unfiltered_ct, species_str(unfiltered_ct));
   if (duplicate_ct) {
     // "At least" since this does not count duplicate IDs absent from the .bim.
-    LOGPRINTF("Warning: At least %" PRIuPTR " duplicate ID%s in --%s file.\n", duplicate_ct, (duplicate_ct == 1)? "" : "s", keep_or_remove_flag_str(flags));
+    LOGERRPRINTF("Warning: At least %" PRIuPTR " duplicate ID%s in --%s file.\n", duplicate_ct, (duplicate_ct == 1)? "" : "s", keep_or_remove_flag_str(flags));
   }
   while (0) {
   keep_or_remove_ret_NOMEM:
@@ -164,7 +164,7 @@ int32_t keep_or_remove(char* fname, char* sorted_ids, uintptr_t sorted_ids_ct, u
     retval = RET_READ_FAIL;
     break;
   keep_or_remove_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   keep_or_remove_ret_ALL_SAMPLES_EXCLUDED:
@@ -314,14 +314,14 @@ int32_t extract_exclude_flag_norange(char* fname, uint32_t* marker_id_htable, ui
   }
   *marker_exclude_ct_ptr = popcount_longs(marker_exclude, unfiltered_marker_ctl);
   if (*marker_exclude_ct_ptr == unfiltered_marker_ct) {
-    LOGPRINTF("Error: No variants remaining after --%s.\n", do_exclude? "exclude" : "extract");
+    LOGERRPRINTF("Error: No variants remaining after --%s.\n", do_exclude? "exclude" : "extract");
     goto extract_exclude_flag_norange_ret_ALL_MARKERS_EXCLUDED;
   }
   unfiltered_marker_ct -= *marker_exclude_ct_ptr; // now filtered count
   LOGPRINTF("--%s: %" PRIuPTR " variant%s remaining.\n", do_exclude? "exclude" : "extract", unfiltered_marker_ct, (unfiltered_marker_ct == 1)? "" : "s");
   if (duplicate_ct) {
     // "At least" since this does not count duplicate IDs absent from the .bim.
-    LOGPRINTF("Warning: At least %" PRIuPTR " duplicate ID%s in --%s file.\n", duplicate_ct, (duplicate_ct == 1)? "" : "s", do_exclude? "exclude" : "extract");
+    LOGERRPRINTF("Warning: At least %" PRIuPTR " duplicate ID%s in --%s file.\n", duplicate_ct, (duplicate_ct == 1)? "" : "s", do_exclude? "exclude" : "extract");
   }
 
   while (0) {
@@ -335,7 +335,7 @@ int32_t extract_exclude_flag_norange(char* fname, uint32_t* marker_id_htable, ui
     retval = RET_READ_FAIL;
     break;
   extract_exclude_flag_norange_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   extract_exclude_flag_norange_ret_ALL_MARKERS_EXCLUDED:
@@ -394,7 +394,7 @@ int32_t filter_attrib(char* fname, char* condition_str, uint32_t* id_htable, uin
 	if (*cond_ptr == ',') {
 	  continue;
 	} else if (*cond_ptr == '-') {
-	  logprint("Error: --attrib condition cannot contain consecutive dashes.\n");
+	  logerrprint("Error: --attrib condition cannot contain consecutive dashes.\n");
 	  goto filter_attrib_ret_INVALID_CMDLINE;
 	}
 	is_neg = 1;
@@ -549,7 +549,7 @@ int32_t filter_attrib(char* fname, char* condition_str, uint32_t* id_htable, uin
     include_ct++;
   }
   if (!include_ct) {
-    logprint("Error: No variants remaining after --attrib.\n");
+    logerrprint("Error: No variants remaining after --attrib.\n");
     retval = RET_ALL_MARKERS_EXCLUDED;
     goto filter_attrib_ret_1;
   }
@@ -567,12 +567,12 @@ int32_t filter_attrib(char* fname, char* condition_str, uint32_t* id_htable, uin
     retval = RET_READ_FAIL;
     break;
   filter_attrib_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
   filter_attrib_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
   filter_attrib_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -633,7 +633,7 @@ int32_t filter_attrib_sample(char* fname, char* condition_str, char* sorted_ids,
 	if (*cond_ptr == ',') {
 	  continue;
 	} else if (*cond_ptr == '-') {
-	  logprint("Error: --attrib-indiv condition cannot contain consecutive dashes.\n");
+	  logerrprint("Error: --attrib-indiv condition cannot contain consecutive dashes.\n");
 	  goto filter_attrib_sample_ret_INVALID_CMDLINE;
 	}
 	is_neg = 1;
@@ -785,7 +785,7 @@ int32_t filter_attrib_sample(char* fname, char* condition_str, char* sorted_ids,
     include_ct++;
   }
   if (!include_ct) {
-    LOGPRINTF("Error: No %s remaining after --attrib-indiv.\n", g_species_plural);
+    LOGERRPRINTF("Error: No %s remaining after --attrib-indiv.\n", g_species_plural);
     retval = RET_ALL_SAMPLES_EXCLUDED;
     goto filter_attrib_sample_ret_1;
   }
@@ -803,12 +803,12 @@ int32_t filter_attrib_sample(char* fname, char* condition_str, char* sorted_ids,
     retval = RET_READ_FAIL;
     break;
   filter_attrib_sample_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
   filter_attrib_sample_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
   filter_attrib_sample_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -932,7 +932,7 @@ int32_t filter_qual_scores(Two_col_params* qual_filter, double qual_min_thresh,
   filter_qual_scores_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --qual-scores file has fewer tokens than expected.\n", line_idx);
   filter_qual_scores_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -961,7 +961,7 @@ uint32_t random_thin_markers(double thin_keep_prob, uintptr_t unfiltered_marker_
     } while (++marker_uidx < marker_uidx_stop);
   }
   if (marker_ct == removed_ct) {
-    logprint("Error: All variants removed by --thin.  Try a higher probability.\n");
+    logerrprint("Error: All variants removed by --thin.  Try a higher probability.\n");
     return 1;
   }
   LOGPRINTF("--thin: %u variant%s removed (%u remaining).\n", removed_ct, (removed_ct == 1)? "" : "s", marker_ct - removed_ct);
@@ -978,7 +978,7 @@ int32_t random_thin_markers_ct(uint32_t thin_keep_ct, uintptr_t unfiltered_marke
   uintptr_t* perm_buf;
   uint32_t marker_idx;
   if (thin_keep_ct > marker_ct) {
-    LOGPRINTF("Error: --thin-count parameter exceeds number of remaining variants.\n");
+    LOGERRPRINTF("Error: --thin-count parameter exceeds number of remaining variants.\n");
     goto random_thin_markers_ct_ret_INVALID_CMDLINE;
   }
   if (wkspace_alloc_ul_checked(&perm_buf, marker_ctl * sizeof(intptr_t))) {
@@ -1026,7 +1026,7 @@ uint32_t random_thin_samples(double thin_keep_prob, uintptr_t unfiltered_sample_
     } while (++sample_uidx < sample_uidx_stop);
   }
   if (sample_ct == removed_ct) {
-    LOGPRINTF("Error: All %s removed by --thin-indiv. Try a higher probability.\n", g_species_plural);
+    LOGERRPRINTF("Error: All %s removed by --thin-indiv. Try a higher probability.\n", g_species_plural);
     return 1;
   }
   LOGPRINTF("--thin-indiv: %u %s removed (%u remaining).\n", removed_ct, (removed_ct==1)? g_species_singular : g_species_plural, sample_ct - removed_ct);
@@ -1043,7 +1043,7 @@ int32_t random_thin_samples_ct(uint32_t thin_keep_ct, uintptr_t unfiltered_sampl
   uintptr_t* perm_buf;
   uint32_t sample_idx;
   if (thin_keep_ct > sample_ct) {
-    LOGPRINTF("Error: --thin-indiv-count parameter exceeds number of remaining %s.\n", g_species_plural);
+    LOGERRPRINTF("Error: --thin-indiv-count parameter exceeds number of remaining %s.\n", g_species_plural);
     goto random_thin_samples_ct_ret_INVALID_CMDLINE;
   }
   if (wkspace_alloc_ul_checked(&perm_buf, sample_ctl * sizeof(intptr_t))) {
@@ -1183,7 +1183,7 @@ int32_t load_oblig_missing(FILE* bedfile, uintptr_t bed_offset, uintptr_t unfilt
     goto load_oblig_missing_ret_READ_FAIL;
   }
   if (!max_cluster_id_len) {
-    LOGPRINTFWW("Warning: --oblig-missing ignored, since no valid blocks were defined in %s.\n", om_ip->sample_fname);
+    LOGERRPRINTFWW("Warning: --oblig-missing ignored, since no valid blocks were defined in %s.\n", om_ip->sample_fname);
     goto load_oblig_missing_ret_1;
   }
   wkspace_left -= topsize;
@@ -1307,11 +1307,11 @@ int32_t load_oblig_missing(FILE* bedfile, uintptr_t bed_offset, uintptr_t unfilt
     goto load_oblig_missing_ret_READ_FAIL;
   }
   if (missing_cluster_ct) {
-    LOGPRINTFWW("Warning: %" PRIuPTR " entr%s in %s had block IDs missing from %s.\n", missing_cluster_ct, (missing_cluster_ct == 1)? "y" : "ies", om_ip->marker_fname, om_ip->sample_fname);
+    LOGERRPRINTFWW("Warning: %" PRIuPTR " entr%s in %s had block IDs missing from %s.\n", missing_cluster_ct, (missing_cluster_ct == 1)? "y" : "ies", om_ip->marker_fname, om_ip->sample_fname);
   }
   om_ip->entry_ct = (uintptr_t)(zc_entries_end - zc_entries);
   if (!om_ip->entry_ct) {
-    LOGPRINTFWW("Warning: --oblig-missing ignored, since %s had no valid entries.\n", om_ip->marker_fname);
+    LOGERRPRINTFWW("Warning: --oblig-missing ignored, since %s had no valid entries.\n", om_ip->marker_fname);
     goto load_oblig_missing_ret_1;
   }
 
@@ -1368,7 +1368,7 @@ int32_t load_oblig_missing(FILE* bedfile, uintptr_t bed_offset, uintptr_t unfilt
     retval = RET_READ_FAIL;
     break;
   load_oblig_missing_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1447,7 +1447,7 @@ int32_t filter_samples_file(char* filtername, char* sorted_sample_ids, uintptr_t
     goto filter_samples_file_ret_READ_FAIL;
   }
   if (!include_ct) {
-    LOGPRINTF("Error: All %s excluded by --filter.\n", g_species_plural);
+    LOGERRPRINTF("Error: All %s excluded by --filter.\n", g_species_plural);
     goto filter_samples_file_ret_ALL_SAMPLES_EXCLUDED;
   }
   LOGPRINTF("--filter: %" PRIuPTR " %s remaining.\n", include_ct, species_str(include_ct));
@@ -1467,7 +1467,7 @@ int32_t filter_samples_file(char* filtername, char* sorted_sample_ids, uintptr_t
   filter_samples_file_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --filter file has fewer tokens than expected.\n", line_idx);
   filter_samples_file_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   filter_samples_file_ret_ALL_SAMPLES_EXCLUDED:
@@ -1665,7 +1665,7 @@ int32_t mind_filter(FILE* bedfile, uintptr_t bed_offset, char* outname, char* ou
   }
   *sample_exclude_ct_ptr += removed_ct;
   if (*sample_exclude_ct_ptr == unfiltered_sample_ct) {
-    LOGPRINTF("Error: All %s removed due to missing genotype data (--mind).\n", g_species_plural);
+    LOGERRPRINTF("Error: All %s removed due to missing genotype data (--mind).\n", g_species_plural);
     LOGPRINTFWW("IDs written to %s .\n", outname);
     goto mind_filter_ret_ALL_SAMPLES_EXCLUDED;
   }
@@ -2668,10 +2668,10 @@ int32_t calc_freqs_and_hwe(FILE* bedfile, char* outname, char* outname_end, uint
   logprint(" done.\n");
   if (hethap_ct) {
     *outname_end = '\0';
-    LOGPRINTFWW("Warning: %" PRIu64 " het. haploid genotype%s present (see %s.hh ); many commands treat these as missing.\n", hethap_ct, (hethap_ct == 1LLU)? "" : "s", outname);
+    LOGERRPRINTFWW("Warning: %" PRIu64 " het. haploid genotype%s present (see %s.hh ); many commands treat these as missing.\n", hethap_ct, (hethap_ct == 1LLU)? "" : "s", outname);
   }
   if (nonmissing_nonmale_y) {
-    logprint("Warning: Nonmissing nonmale Y chromosome genotype(s) present; many commands\ntreat these as missing.\n");
+    logerrprint("Warning: Nonmissing nonmale Y chromosome genotype(s) present; many commands\ntreat these as missing.\n");
     *hh_exists_ptr |= Y_FIX_NEEDED;
   }
   if (nonmissing_rate_tot <= 0.9999995 * ((double)((intptr_t)nonmissing_rate_tot_max))) {
@@ -3345,10 +3345,10 @@ uint32_t enforce_hwe_threshold(double hwe_thresh, uintptr_t unfiltered_marker_ct
     }
   }
   if (((uint64_t)max_obs) * 9 > ((uint64_t)min_obs) * 10) {
-    logprint("Warning: --hwe observation counts vary by more than 10%.  Consider using\n--geno, and/or applying different p-value thresholds to distinct subsets of\nyour data.\n");
+    logerrprint("Warning: --hwe observation counts vary by more than 10%.  Consider using\n--geno, and/or applying different p-value thresholds to distinct subsets of\nyour data.\n");
   }
   if (marker_ct == removed_ct) {
-    logprint("Error: All variants removed due to Hardy-Weinberg exact test (--hwe).\n");
+    logerrprint("Error: All variants removed due to Hardy-Weinberg exact test (--hwe).\n");
     return 1;
   }
   LOGPRINTF("--hwe: %u variant%s removed due to Hardy-Weinberg exact test.\n", removed_ct, (removed_ct == 1)? "" : "s");
@@ -3384,7 +3384,7 @@ uint32_t enforce_minor_allele_thresholds(double min_maf, double max_maf, uintptr
   }
   removed_ct = popcount_longs(marker_exclude, unfiltered_marker_ctl) - (*marker_exclude_ct_ptr);
   if (marker_ct == removed_ct) {
-    logprint("Error: All variants removed due to minor allele threshold(s)\n(--maf/--max-maf/--mac/--max-mac).\n");
+    logerrprint("Error: All variants removed due to minor allele threshold(s)\n(--maf/--max-maf/--mac/--max-mac).\n");
     return 1;
   }
   LOGPRINTFWW("%u variant%s removed due to minor allele threshold(s) (--maf/--max-maf/--mac/--max-mac).\n", removed_ct, (removed_ct == 1)? "" : "s");
diff --git a/plink_glm.c b/plink_glm.c
index 27759b0..ebf3e32 100644
--- a/plink_glm.c
+++ b/plink_glm.c
@@ -305,7 +305,7 @@ int32_t glm_scan_conditions(char* condition_mname, char* condition_fname, uintpt
   if (condition_mname) {
     ii = get_uidx_from_unsorted(condition_mname, marker_exclude, marker_ct, marker_ids, max_marker_id_len);
     if (ii == -1) {
-      LOGPRINTFWW("Warning: --condition variant ID '%s' not found.\n", condition_mname);
+      LOGERRPRINTFWW("Warning: --condition variant ID '%s' not found.\n", condition_mname);
       return 0;
     }
     condition_ct = 1;
@@ -348,7 +348,7 @@ int32_t glm_scan_conditions(char* condition_mname, char* condition_fname, uintpt
 	  miss_ct++;
 	} else {
 	  if (is_set(already_seen, ii)) {
-	    LOGPRINTFWW("Error: Duplicate variant '%s' in --condition-list file.\n", bufptr);
+	    LOGERRPRINTFWW("Error: Duplicate variant '%s' in --condition-list file.\n", bufptr);
 	    goto glm_scan_conditions_ret_INVALID_FORMAT;
 	  }
 	  if (condition_ct == condition_ct_max) {
@@ -367,7 +367,7 @@ int32_t glm_scan_conditions(char* condition_mname, char* condition_fname, uintpt
       condition_uidxs = (uint32_t*)malloc(condition_ct * sizeof(int32_t));
       memcpy(condition_uidxs, condition_uidxs_tmp, condition_ct * sizeof(int32_t));
     } else if (!miss_ct) {
-      logprint("Warning: --condition-list file is empty.\n");
+      logerrprint("Warning: --condition-list file is empty.\n");
       goto glm_scan_conditions_ret_1;
     }
     if (miss_ct) {
@@ -452,7 +452,7 @@ int32_t glm_scan_conditions(char* condition_mname, char* condition_fname, uintpt
     retval = RET_READ_FAIL;
     break;
   glm_scan_conditions_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   glm_scan_conditions_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -3774,19 +3774,19 @@ int32_t glm_common_init(FILE* bedfile, uintptr_t bed_offset, uint32_t glm_modifi
   g_male_x_01 = 0;
   if (!glm_xchr_model) {
     if (is_set(chrom_info_ptr->haploid_mask, 0)) {
-      logprint("Error: --xchr-model 0 cannot be used with haploid genomes.\n");
+      logerrprint("Error: --xchr-model 0 cannot be used with haploid genomes.\n");
       goto glm_common_init_ret_INVALID_CMDLINE;
     }
     uii = count_non_autosomal_markers(chrom_info_ptr, marker_exclude, 1, 1);
     if (uii) {
       if (is_set_test) {
-	logprint("Error: --linear/--logistic set-test cannot be used with --xchr-model 0.\n");
+	logerrprint("Error: --linear/--logistic set-test cannot be used with --xchr-model 0.\n");
 	goto glm_common_init_ret_INVALID_CMDLINE;
       }
       LOGPRINTF("Excluding %u nonautosomal variant%s from --linear/--logistic analysis\n(--xchr-model 0).\n", uii, (uii == 1)? "" : "s");
       marker_initial_ct -= uii;
       if (!marker_initial_ct) {
-	logprint("Error: No variants remaining for --linear/--logistic analysis.\n");
+	logerrprint("Error: No variants remaining for --linear/--logistic analysis.\n");
 	goto glm_common_init_ret_INVALID_CMDLINE;
       }
     }
@@ -3884,10 +3884,10 @@ int32_t glm_common_init(FILE* bedfile, uintptr_t bed_offset, uint32_t glm_modifi
       }
     } else {
       if (sex_covar_everywhere) {
-        LOGPRINTFWW("Warning: Ignoring --linear/--logistic 'sex' modifier%s since sex is%cinvariant.\n", x_sex_interaction? " and --xchr-model 3" : "", x_sex_interaction? '\n' : ' ');
+        LOGERRPRINTFWW("Warning: Ignoring --linear/--logistic 'sex' modifier%s since sex is%cinvariant.\n", x_sex_interaction? " and --xchr-model 3" : "", x_sex_interaction? '\n' : ' ');
         sex_covar_everywhere = 0;
       } else if (x_sex_interaction) {
-        logprint("Warning: Ignoring --xchr-model 3 since sex is invariant.\n");
+        logerrprint("Warning: Ignoring --xchr-model 3 since sex is invariant.\n");
       }
       x_sex_interaction = 0;
     }
@@ -3941,7 +3941,7 @@ int32_t glm_common_init(FILE* bedfile, uintptr_t bed_offset, uint32_t glm_modifi
     numeric_range_list_to_bitfield(parameters_range_list_ptr, param_raw_ct_max, active_params, 0, 1);
     if ((!(active_params[0] & 2)) && ((!np_diploid_raw) || (active_params[0] & 4)) && ((!covar_interactions) || ((!popcount_bit_idx(active_params, interaction_start_idx, sex_start_idx)) && ((!variation_in_sex) || (!popcount_bit_idx(active_params, sex_start_idx + 1, param_raw_ct_max)))))) {
       // force the user to explicitly use no-snp if that's their intention
-      logprint("Error: --parameters must retain at least one dosage-dependent variable.  To\nperform one-off regression(s), use the --linear 'no-snp' modifier instead.\n");
+      logerrprint("Error: --parameters must retain at least one dosage-dependent variable.  To\nperform one-off regression(s), use the --linear 'no-snp' modifier instead.\n");
       goto glm_common_init_ret_INVALID_CMDLINE;
     }
     param_ct_max = popcount_longs(active_params, param_raw_ctl);
@@ -3982,7 +3982,7 @@ int32_t glm_common_init(FILE* bedfile, uintptr_t bed_offset, uint32_t glm_modifi
   if (IS_SET(active_params, 1)) {
     max_param_name_len = 4;
   } else if (mtest_adjust) {
-    logprint("Error: --adjust cannot be used when --parameters excludes the main effect.\n");
+    logerrprint("Error: --adjust cannot be used when --parameters excludes the main effect.\n");
     goto glm_common_init_ret_INVALID_CMDLINE;
   }
   if (hide_covar) {
@@ -3994,7 +3994,7 @@ int32_t glm_common_init(FILE* bedfile, uintptr_t bed_offset, uint32_t glm_modifi
       if (tests_range_list_ptr->name_ct || (glm_modifier & GLM_TEST_ALL)) {
         param_idx_end = 1;
       } else {
-        logprint("Error: 'hide-covar' modifier suppresses all output due to --parameters setting.\n");
+        logerrprint("Error: 'hide-covar' modifier suppresses all output due to --parameters setting.\n");
         goto glm_common_init_ret_INVALID_CMDLINE;
       }
     }
@@ -4153,15 +4153,15 @@ int32_t glm_common_init(FILE* bedfile, uintptr_t bed_offset, uint32_t glm_modifi
       wkspace_reset(g_joint_test_params);
       g_joint_test_params = NULL;
       constraint_ct_max = 0;
-      logprint("Warning: Ignoring --tests since too few parameter indices are in range.\n");
+      logerrprint("Warning: Ignoring --tests since too few parameter indices are in range.\n");
     }
   }
   if (covar_interactions && do_perms && ((!constraint_ct_max) || uii)) {
-    logprint("Error: --linear/--logistic 'interaction' modifier cannot be used with\npermutation except with --tests.\n");
+    logerrprint("Error: --linear/--logistic 'interaction' modifier cannot be used with\npermutation except with --tests.\n");
     goto glm_common_init_ret_INVALID_CMDLINE;
   }
   if (do_perms && (!IS_SET(active_params, 1)) && (!constraint_ct_max)) {
-    logprint("Error: --linear/--logistic permutation test cannot occur when --parameters\nexcludes the main effect and no joint test is active.\n");
+    logerrprint("Error: --linear/--logistic permutation test cannot occur when --parameters\nexcludes the main effect and no joint test is active.\n");
     goto glm_common_init_ret_INVALID_CMDLINE;
   }
 
@@ -4693,9 +4693,9 @@ int32_t glm_linear_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
     goto glm_linear_assoc_ret_1;
   }
   if (sample_valid_ct <= param_ct_max) {
-    logprint("Warning: Skipping --linear since # variables >= # samples.\n");
+    logerrprint("Warning: Skipping --linear since # variables >= # samples.\n");
     if (pheno_nm_ct > param_ct_max) {
-      logprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
+      logerrprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
     }
     goto glm_linear_assoc_ret_1;
   }
@@ -5057,7 +5057,7 @@ int32_t glm_linear_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
       dgels_(&dgels_trans, &dgels_m, &dgels_n, &dgels_nrhs, g_linear_mt[0].dgels_a, &dgels_m, g_linear_mt[0].dgels_b, &dgels_ldb, &dxx, &g_dgels_lwork, &dgels_info);
       // todo: support linking to 64-bit LAPACK on Linux
       if (dxx > 2147483647.0) {
-	logprint("Error: Multiple linear regression problem too large for current LAPACK version.\n");
+	logerrprint("Error: Multiple linear regression problem too large for current LAPACK version.\n");
 	retval = RET_CALC_NOT_YET_SUPPORTED;
 	goto glm_linear_assoc_ret_1;
       }
@@ -5768,11 +5768,11 @@ int32_t glm_linear_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
     retval = RET_WRITE_FAIL;
     break;
   glm_linear_assoc_ret_NO_PERMUTATION_CLUSTERS:
-    logprint("Error: No size 2+ clusters for permutation test.\n");
+    logerrprint("Error: No size 2+ clusters for permutation test.\n");
     retval = RET_INVALID_CMDLINE;
     break;
   glm_linear_assoc_ret_PHENO_CONSTANT:
-    logprint("Warning: Skipping --linear since phenotype is constant.\n");
+    logerrprint("Warning: Skipping --linear since phenotype is constant.\n");
     break;
   glm_linear_assoc_ret_THREAD_CREATE_FAIL:
     retval = RET_THREAD_CREATE_FAIL;
@@ -6201,7 +6201,7 @@ int32_t glm_logistic_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
   uint32_t ukk;
   numbuf[0] = ' ';
   if (pheno_nm_ct < 2) {
-    logprint("Warning: Skipping --logistic since less than two phenotypes are present.\n");
+    logerrprint("Warning: Skipping --logistic since less than two phenotypes are present.\n");
     goto glm_logistic_assoc_ret_1;
   }
   if ((chrom_info_ptr->mt_code != -1) && is_set(chrom_info_ptr->chrom_mask, chrom_info_ptr->mt_code)) {
@@ -6231,9 +6231,9 @@ int32_t glm_logistic_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
     goto glm_logistic_assoc_ret_1;
   }
   if (sample_valid_ct <= param_ct_max) {
-    logprint("Warning: Skipping --logistic since # variables >= # samples.\n");
+    logerrprint("Warning: Skipping --logistic since # variables >= # samples.\n");
     if (pheno_nm_ct > param_ct_max) {
-      logprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
+      logerrprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
     }
     goto glm_logistic_assoc_ret_1;
   }
@@ -7194,11 +7194,11 @@ int32_t glm_logistic_assoc(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
     retval = RET_WRITE_FAIL;
     break;
   glm_logistic_assoc_ret_NO_PERMUTATION_CLUSTERS:
-    logprint("Error: No size 2+ clusters for permutation test.\n");
+    logerrprint("Error: No size 2+ clusters for permutation test.\n");
     retval = RET_INVALID_CMDLINE;
     break;
   glm_logistic_assoc_ret_PHENO_CONSTANT:
-    logprint("Warning: Skipping --linear/--logistic since phenotype is constant.\n");
+    logerrprint("Warning: Skipping --linear/--logistic since phenotype is constant.\n");
     break;
   glm_logistic_assoc_ret_THREAD_CREATE_FAIL:
     retval = RET_THREAD_CREATE_FAIL;
@@ -7310,7 +7310,7 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
   uint32_t slen;
   int32_t ii;
   if (hide_covar && (!tests_range_list_ptr->name_ct) && (!(glm_modifier & GLM_TEST_ALL))) {
-    logprint("Error: --linear hide-covar no-snp produces no output.\n");
+    logerrprint("Error: --linear hide-covar no-snp produces no output.\n");
     goto glm_linear_nosnp_ret_INVALID_CMDLINE;
   }
   if (glm_init_load_mask(sample_exclude, pheno_nm, covar_nm, sample_ct, unfiltered_sample_ctv2, &load_mask)) {
@@ -7379,7 +7379,7 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
       bitfield_and(load_mask, sex_nm, unfiltered_sample_ctl);
       sample_valid_ct = popcount_longs(load_mask, unfiltered_sample_ctl);
     } else {
-      logprint("Warning: Ignoring --linear 'sex' modifier since sex is invariant.\n");
+      logerrprint("Warning: Ignoring --linear 'sex' modifier since sex is invariant.\n");
     }
   }
   sample_valid_ctv2 = 2 * ((sample_valid_ct + BITCT - 1) / BITCT);
@@ -7421,12 +7421,12 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
     param_ct = param_raw_ct;
   }
   if (param_ct == 1) {
-    logprint("Warning: Skipping --linear since the intercept is the only variable.\n");
+    logerrprint("Warning: Skipping --linear since the intercept is the only variable.\n");
     goto glm_linear_nosnp_ret_1;
   } else if (sample_valid_ct <= param_ct) {
-    logprint("Warning: Skipping --linear since # variables >= # samples.\n");
+    logerrprint("Warning: Skipping --linear since # variables >= # samples.\n");
     if (pheno_nm_ct > param_ct) {
-      logprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
+      logerrprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
     }
     goto glm_linear_nosnp_ret_1;
   }
@@ -7476,7 +7476,7 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
     } else {
       constraint_ct = 0;
       aligned_free_null(&joint_test_params);
-      logprint("Warning: Ignoring --tests since fewer than two parameter indices are in range.\n");
+      logerrprint("Warning: Ignoring --tests since fewer than two parameter indices are in range.\n");
     }
   }
 
@@ -7665,7 +7665,7 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
   if (ii == -1) {
     goto glm_linear_nosnp_ret_NOMEM;
   } else if (ii == 1) {
-    logprint("Warning: Skipping --linear no-snp since VIF check failed.\n");
+    logerrprint("Warning: Skipping --linear no-snp since VIF check failed.\n");
     goto glm_linear_nosnp_ret_1;
   }
 
@@ -7718,7 +7718,7 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
     // maybe this can't actually happen, but just in case...
     // (todo: update this, and all the other matrix logic, once LAPACK can
     // handle 64-bit integers; currently that's on their wishlist)
-    logprint("Error: Multiple linear regression problem too large for current LAPACK version.\n");
+    logerrprint("Error: Multiple linear regression problem too large for current LAPACK version.\n");
     retval = RET_CALC_NOT_YET_SUPPORTED;
     goto glm_linear_nosnp_ret_1;
   }
@@ -7730,7 +7730,7 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
 
   dgels_(&dgels_trans, &dgels_m, &dgels_n, &dgels_nrhs, dgels_a, &dgels_m, dgels_b, &dgels_ldb, dgels_work, &dgels_lwork, &dgels_info);
   if (dgels_info) {
-    logprint("Warning: Skipping --linear no-snp since regression failed.\n");
+    logerrprint("Warning: Skipping --linear no-snp since regression failed.\n");
     goto glm_linear_nosnp_ret_1;
   }
 
@@ -7767,11 +7767,11 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
 
   transpose_copy(param_ct, sample_valid_ct, covars_cov_major, covars_sample_major);
   if (glm_linear(1, param_ct, sample_valid_ct, 0, NULL, 0, 0, 0, covars_cov_major, covars_sample_major, g_pheno_d2, dgels_b, param_2d_buf, mi_buf, param_2d_buf2, regression_results, constraint_ct, constraints_con_major, param_df_buf, param_df_buf2, df_df_buf, df_buf, &perm_fail_ct, perm_fails) || perm_fail_ct) {
-    logprint("Warning: Skipping --linear no-snp due to multicollinearity.\n");
+    logerrprint("Warning: Skipping --linear no-snp due to multicollinearity.\n");
     goto glm_linear_nosnp_ret_1;
   }
   if (constraint_ct && (regression_results[param_ct - 1] == -9)) {
-    logprint("Warning: Ignoring --tests due to regression failure.\n");
+    logerrprint("Warning: Ignoring --tests due to regression failure.\n");
     constraint_ct = 0;
   }
 
@@ -8079,12 +8079,12 @@ int32_t glm_linear_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
     retval = RET_THREAD_CREATE_FAIL;
     break;
   glm_linear_nosnp_ret_NO_PERMUTATION_CLUSTERS:
-    logprint("Error: No size 2+ clusters for permutation test.\n");
+    logerrprint("Error: No size 2+ clusters for permutation test.\n");
   glm_linear_nosnp_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
   glm_linear_nosnp_ret_PHENO_CONSTANT:
-    logprint("Warning: Skipping --linear since phenotype is constant.\n");
+    logerrprint("Warning: Skipping --linear since phenotype is constant.\n");
     break;
   }
  glm_linear_nosnp_ret_1:
@@ -8190,7 +8190,7 @@ int32_t glm_logistic_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
   uint32_t ujj;
   uint32_t slen;
   if (hide_covar && (!tests_range_list_ptr->name_ct) && (!(glm_modifier & GLM_TEST_ALL))) {
-    logprint("Error: --logistic hide-covar no-snp produces no output.\n");
+    logerrprint("Error: --logistic hide-covar no-snp produces no output.\n");
     goto glm_logistic_nosnp_ret_INVALID_CMDLINE;
   }
   if (glm_init_load_mask(sample_exclude, pheno_nm, covar_nm, sample_ct, unfiltered_sample_ctv2, &load_mask)) {
@@ -8259,7 +8259,7 @@ int32_t glm_logistic_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
       bitfield_and(load_mask, sex_nm, unfiltered_sample_ctl);
       sample_valid_ct = popcount_longs(load_mask, unfiltered_sample_ctl);
     } else {
-      logprint("Warning: Ignoring --logistic 'sex' modifier since sex is invariant.\n");
+      logerrprint("Warning: Ignoring --logistic 'sex' modifier since sex is invariant.\n");
     }
   }
   sample_valid_cta4 = (sample_valid_ct + 3) & (~(3 * ONELU));
@@ -8303,12 +8303,12 @@ int32_t glm_logistic_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
   }
   param_cta4 = (param_ct + 3) & (~(3 * ONELU));
   if (param_ct == 1) {
-    logprint("Warning: Skipping --logistic since the intercept is the only variable.\n");
+    logerrprint("Warning: Skipping --logistic since the intercept is the only variable.\n");
     goto glm_logistic_nosnp_ret_1;
   } else if (sample_valid_ct <= param_ct) {
-    logprint("Warning: Skipping --logistic since # variables >= # samples.\n");
+    logerrprint("Warning: Skipping --logistic since # variables >= # samples.\n");
     if (pheno_nm_ct > param_ct) {
-      logprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
+      logerrprint("(Check your covariates--all samples with at least one missing covariate are\nexcluded from this analysis.)\n");
     }
     goto glm_logistic_nosnp_ret_1;
   }
@@ -8358,7 +8358,7 @@ int32_t glm_logistic_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
     } else {
       constraint_ct = 0;
       aligned_free_null(&joint_test_params);
-      logprint("Warning: Ignoring --tests since fewer than two parameter indices are in range.\n");
+      logerrprint("Warning: Ignoring --tests since fewer than two parameter indices are in range.\n");
     }
   }
 
@@ -8568,11 +8568,11 @@ int32_t glm_logistic_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
 
   fill_float_zero(coef, param_cta4);
   if (glm_logistic(1, param_ct, sample_valid_ct, 0, NULL, covars_cov_major, g_perm_vecs, coef, pp, sample_1d_buf, pheno_buf, param_1d_buf, param_1d_buf2, param_2d_buf, param_2d_buf2, regression_results, constraint_ct, constraints_con_major, param_1d_dbuf, param_2d_dbuf, param_2d_dbuf2, param_df_dbuf, df_df_dbuf, mi_buf, df_dbuf, perm_fails)) {
-    logprint("Warning: Skipping --logistic no-snp due to multicollinearity.\n");
+    logerrprint("Warning: Skipping --logistic no-snp due to multicollinearity.\n");
     goto glm_logistic_nosnp_ret_1;
   }
   if (constraint_ct && (regression_results[param_ct - 1] == -9)) {
-    logprint("Warning: Ignoring --tests due to regression failure.\n");
+    logerrprint("Warning: Ignoring --tests due to regression failure.\n");
     constraint_ct = 0;
   }
 
@@ -8853,12 +8853,12 @@ int32_t glm_logistic_nosnp(pthread_t* threads, FILE* bedfile, uintptr_t bed_offs
     retval = RET_THREAD_CREATE_FAIL;
     break;
   glm_logistic_nosnp_ret_NO_PERMUTATION_CLUSTERS:
-    logprint("Error: No size 2+ clusters for permutation test.\n");
+    logerrprint("Error: No size 2+ clusters for permutation test.\n");
   glm_logistic_nosnp_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
   glm_logistic_nosnp_ret_PHENO_CONSTANT:
-    logprint("Warning: Skipping --logistic since phenotype is constant.\n");
+    logerrprint("Warning: Skipping --logistic since phenotype is constant.\n");
     break;
   }
  glm_logistic_nosnp_ret_1:
diff --git a/plink_help.c b/plink_help.c
index a09b2ff..ec8daa0 100644
--- a/plink_help.c
+++ b/plink_help.c
@@ -383,6 +383,7 @@ int32_t disp_help(uint32_t param_ct, char** argv) {
 "           | compound-genotypes | fastphase{-1chr} | HV{-1chr} | lgen{-ref} |\n"
 "           list | oxford | rlist | structure | transpose | vcf | vcf-fid |\n"
 "           vcf-iid> <tab | tabx | spacex | bgz | gen-gz> <include-alt>\n"
+"           <omit-nonmale-y>\n"
 "    Create a new text fileset with all filters applied.  By default, the\n"
 "    fileset consists of a .ped and a .map file, readable with --file.\n"
 "    * The '12' modifier causes A1 (usually minor) alleles to be coded as '1'\n"
@@ -414,7 +415,8 @@ int32_t disp_help(uint32_t param_ct, char** argv) {
 "      to be generated, while 'lgen-ref' generates a (usually) smaller\n"
 "      long-format fileset loadable with --lfile + --reference.\n"
 "    * The 'list' modifier creates a genotype-based list, while 'rlist' creates\n"
-"      a rare-genotype fileset.\n"
+"      a rare-genotype fileset.  With these formats, the 'omit-nonmale-y'\n"
+"      modifier causes nonmale genotypes to be omitted on the Y chromosome.\n"
 "    * 'oxford' causes an Oxford-format .gen + .sample fileset to be generated.\n"
 "      If you also include the 'gen-gz' modifier, the .gen file is gzipped.\n"
 "    * The 'structure' modifier causes a Structure-format file to be generated.\n"
@@ -529,8 +531,9 @@ int32_t disp_help(uint32_t param_ct, char** argv) {
 "    Hardy-Weinberg Equilibrium.\n\n"
 	       );
     help_print("mendel", &help_ctrl, 1,
-"  --mendel\n"
-"    Generate a Mendel error report.\n\n"
+"  --mendel <summaries-only>\n"
+"    Generate a Mendel error report.  The 'summaries-only' modifier causes the\n"
+"    .mendel file (listing every single error) to be skipped.\n\n"
 	       );
     help_print("het\tibc", &help_ctrl, 1,
 "  --het <small-sample> <gz>\n"
@@ -2098,6 +2101,10 @@ int32_t disp_help(uint32_t param_ct, char** argv) {
 	       );
     help_print("threads\tthread-num\tnum_threads", &help_ctrl, 0,
 "  --threads [val]    : Set maximum number of concurrent threads.\n"
+"                       This has one known limitation: some BLAS/LAPACK linear\n"
+"                       algebra operations are multithreaded in a way that PLINK\n"
+"                       cannot control.  If this is problematic, you should\n"
+"                       recompile against single-threaded BLAS/LAPACK.\n"
 	       );
     help_print("d\tsnps", &help_ctrl, 0,
 "  --d [char]         : Change variant/covariate range delimiter (normally '-').\n"
diff --git a/plink_homozyg.c b/plink_homozyg.c
index 2f7325a..09caa89 100644
--- a/plink_homozyg.c
+++ b/plink_homozyg.c
@@ -2,6 +2,9 @@
 
 #include "plink_homozyg.h"
 
+// This implementation's use of unfiltered indices is idiotic; it should be
+// rewritten to be based on post-filtering variant indices ASAP.
+
 void homozyg_init(Homozyg_info* homozyg_ptr) {
   homozyg_ptr->modifier = 0;
   homozyg_ptr->min_snp = 100;
@@ -535,7 +538,10 @@ int32_t write_main_roh_reports(char* outname, char* outname_end, uintptr_t* mark
       kb_tot /= (double)((int32_t)cur_roh_ct);
     }
     wptr = width_force(8, wptr, double_g_write(wptr, kb_tot));
-    wptr = memcpya(wptr, " \n", 2);
+    if (cur_roh_ct) {
+      *wptr++ = ' ';
+    }
+    *wptr++ = '\n';
     if (fwrite_checked(tbuf, wptr - tbuf, outfile_indiv)) {
       goto write_main_roh_reports_ret_WRITE_FAIL;
     }
@@ -592,6 +598,11 @@ int32_t write_main_roh_reports(char* outname, char* outname_end, uintptr_t* mark
     cur_roh_ct = 0; // unaff ct
     uii = 0; // aff ct
     for (marker_uidx1 = chrom_start; marker_uidx1 < chrom_len; marker_uidx1++) {
+      // bugfix: excluded markers can actually be at the end of a ROH.
+      if (pheno_c) {
+	uii += roh_ct_aff_adj[marker_uidx1 - chrom_start];
+      }
+      cur_roh_ct += roh_ct_unaff_adj[marker_uidx1 - chrom_start];
       if (IS_SET(marker_exclude, marker_uidx1)) {
 	continue;
       }
@@ -602,10 +613,8 @@ int32_t write_main_roh_reports(char* outname, char* outname_end, uintptr_t* mark
       if (!pheno_c) {
         wptr = &(wptr_bp1[20]);
       } else {
-	uii += roh_ct_aff_adj[marker_uidx1 - chrom_start];
         wptr = uint32_writew8x(&(wptr_bp1[11]), uii, ' ');
       }
-      cur_roh_ct += roh_ct_unaff_adj[marker_uidx1 - chrom_start];
       if (cur_roh_ct + uii > max_pool_size) {
         max_pool_size = cur_roh_ct + uii;
       }
@@ -743,9 +752,9 @@ void initialize_roh_slot(uint32_t* cur_roh, uint32_t chrom_start, uint32_t* mark
   uint32_t uii;
 
   // given an interval [a, b], when we can't just use the half-open convention
-  // everywhere, "last" means b and "end" means b+1.  (yeah, I should clean up
-  // the huge unfiltered index vs. filtered index mess and remove the need to
-  // deviate from half-open.)
+  // everywhere, "last" means b and "end" means b+1.  (yeah, it wouldn't have
+  // been necessary to make this distinction at all if I hadn't used unfiltered
+  // indices where they didn't belong.)
   *roh_slot_cidx_start = cidx_first;
   *roh_slot_cidx_end = cidx_last + 1;
   *roh_slot_end_uidx = cur_roh[1] + 1;
@@ -2730,10 +2739,10 @@ int32_t calc_homozyg(Homozyg_info* hp, FILE* bedfile, uintptr_t bed_offset, uint
   LOGPRINTFWW("Results saved to %s.hom + %s.hom.indiv + %s.hom.summary .\n", outname, outname, outname);
   if (hp->modifier & (HOMOZYG_GROUP | HOMOZYG_GROUP_VERBOSE)) {
     if (max_pool_size < hp->pool_size_min) {
-      LOGPRINTF("Warning: Skipping --homozyg group%s report since there are no pools.\n", (hp->modifier & HOMOZYG_GROUP_VERBOSE)? "-verbose" : "");
+      LOGERRPRINTF("Warning: Skipping --homozyg group%s report since there are no pools.\n", (hp->modifier & HOMOZYG_GROUP_VERBOSE)? "-verbose" : "");
 #ifndef __LP64__
     } else if (max_pool_size > 65536) {
-      logprint("Error: 32-bit " PROG_NAME_STR "'s --homozyg group cannot handle a pool of size >65536.\n");
+      logerrprint("Error: 32-bit " PROG_NAME_STR "'s --homozyg group cannot handle a pool of size >65536.\n");
       goto calc_homozyg_ret_NOMEM;
 #endif
     } else {
diff --git a/plink_lasso.c b/plink_lasso.c
index faf8b6e..3908a86 100644
--- a/plink_lasso.c
+++ b/plink_lasso.c
@@ -194,7 +194,7 @@ int32_t lasso_bigmem(FILE* bedfile, uintptr_t bed_offset, uintptr_t* marker_excl
   *polymorphic_marker_ct_ptr = polymorphic_marker_ct;
   if (!polymorphic_marker_ct) {
     putchar('\n');
-    logprint("Warning: Skipping --lasso since no polymorphic loci are present.\n");
+    logerrprint("Warning: Skipping --lasso since no polymorphic loci are present.\n");
     return 0;
   }
   col_ct = covar_ct + polymorphic_marker_ct;
@@ -246,7 +246,8 @@ int32_t lasso_bigmem(FILE* bedfile, uintptr_t bed_offset, uintptr_t* marker_excl
     }
   }
   if (lambda_min >= lambda_max) {
-    logprint("\nError: min lambda >= max lambda.\n");
+    logprint("\n");
+    logerrprint("Error: min lambda >= max lambda.\n");
     goto lasso_bigmem_ret_INVALID_CMDLINE;
   }
   loghi = log(lambda_max);
@@ -344,7 +345,7 @@ int32_t lasso_bigmem(FILE* bedfile, uintptr_t bed_offset, uintptr_t* marker_excl
     retval = RET_READ_FAIL;
     break;
   lasso_bigmem_ret_CONST_COVAR:
-    logprint("Error: --lasso covariate is constant.\n");
+    logerrprint("Error: --lasso covariate is constant.\n");
   lasso_bigmem_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -578,7 +579,7 @@ int32_t lasso_smallmem(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset,
     }
   }
   if (!(polymorphic_marker_ct + partial_marker_idx)) {
-    logprint("Warning: Skipping --lasso since no polymorphic markers are present.\n");
+    logerrprint("Warning: Skipping --lasso since no polymorphic markers are present.\n");
     return 0;
   }
   if (rand_matrix) {
@@ -606,7 +607,8 @@ int32_t lasso_smallmem(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset,
   col_ctl = (col_ct + (BITCT - 1)) / BITCT;
   *xhat_ptr = xhat;
   if (lambda_min >= lambda_max) {
-    logprint("\nError: min lambda >= max lambda.\n");
+    logprint("\n");
+    logerrprint("Error: min lambda >= max lambda.\n");
     goto lasso_smallmem_ret_INVALID_CMDLINE;
   }
   if (wkspace_alloc_ul_checked(&active_set, col_ctl * sizeof(intptr_t)) ||
@@ -756,7 +758,7 @@ int32_t lasso_smallmem(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset,
     retval = RET_READ_FAIL;
     break;
   lasso_smallmem_ret_CONST_COVAR:
-    logprint("Error: --lasso covariate is constant.\n");
+    logerrprint("Error: --lasso covariate is constant.\n");
   lasso_smallmem_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -825,9 +827,9 @@ int32_t lasso(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* out
   }
   if (sample_valid_ct < 2) {
     if (pheno_nm_ct < 2) {
-      logprint("Warning: Skipping --lasso since less than two phenotypes are present.\n");
+      logerrprint("Warning: Skipping --lasso since less than two phenotypes are present.\n");
     } else {
-      logprint("Warning: Skipping --lasso since too many samples have missing covariates.\n");
+      logerrprint("Warning: Skipping --lasso since too many samples have missing covariates.\n");
     }
     goto lasso_ret_1;
   }
@@ -895,7 +897,7 @@ int32_t lasso(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* out
     dyy = dxx;
   }
   if (dyy * ((double)((intptr_t)sample_valid_ct)) == dxx * dxx) {
-    logprint("Warning: Skipping --lasso since phenotype is constant.\n");
+    logerrprint("Warning: Skipping --lasso since phenotype is constant.\n");
     goto lasso_ret_1;
   }
   dzz = dxx / ((double)((intptr_t)sample_valid_ct)); // mean
@@ -915,7 +917,7 @@ int32_t lasso(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset, char* out
   }
   if (select_covars && select_covars_range_list_ptr->name_ct) {
     if (!covar_ct) {
-      logprint("Error: No covariates loaded for --lasso-select-covars.\n");
+      logerrprint("Error: No covariates loaded for --lasso-select-covars.\n");
       goto lasso_ret_INVALID_CMDLINE;
     }
     retval = string_range_list_to_bitfield_alloc(covar_names, covar_ct, max_covar_name_len, select_covars_range_list_ptr, &select_covars_bitfield, "lasso-select-covars", "--covar file");
diff --git a/plink_ld.c b/plink_ld.c
index 97eaa9c..a382a7d 100644
--- a/plink_ld.c
+++ b/plink_ld.c
@@ -818,7 +818,7 @@ int32_t ld_prune(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t m
   uint32_t bsearch_cur;
   double prune_ld_thresh;
   if (founder_ct < 2) {
-    LOGPRINTF("Warning: Skipping --indep%s since there are less than two founders.\n(--make-founders may come in handy here.)\n", pairwise? "-pairwise" : "");
+    LOGERRPRINTF("Warning: Skipping --indep%s since there are less than two founders.\n(--make-founders may come in handy here.)\n", pairwise? "-pairwise" : "");
     goto ld_prune_ret_1;
   }
   if (is_set(chrom_info_ptr->chrom_mask, 0)) {
@@ -833,7 +833,7 @@ int32_t ld_prune(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t m
     LOGPRINTF("--indep%s: Ignoring %" PRIuPTR " chromosome 0 variant%s.\n", pairwise? "-pairwise" : "", ulii, (ulii == 1)? "" : "s");
   }
   if (marker_ct < 2) {
-    LOGPRINTF("Error: Too few valid variants for --indep%s.\n", pairwise? "-pairwise" : "");
+    LOGERRPRINTF("Error: Too few valid variants for --indep%s.\n", pairwise? "-pairwise" : "");
     goto ld_prune_ret_INVALID_FORMAT;
   }
 
@@ -846,7 +846,7 @@ int32_t ld_prune(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t m
     if (founder_ct + nonmale_founder_ct > 0x7fffffff) {
       // no, this shouldn't ever happen, but may as well document that there
       // theoretically is a 32-bit integer range issue here
-      logprint("Error: Too many founders for --indep[-pairwise] + --ld-xchr 3.\n");
+      logerrprint("Error: Too many founders for --indep[-pairwise] + --ld-xchr 3.\n");
       goto ld_prune_ret_1;
     }
   }
@@ -866,7 +866,7 @@ int32_t ld_prune(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t m
     if (window_max > 46340) {
       // todo: check what LAPACK's matrix inversion limit actually is.  Guess
       // sqrt(2^31 - 1) for now.
-      logprint("Error: --indep does not currently support window sizes > 46340.\n");
+      logerrprint("Error: --indep does not currently support window sizes > 46340.\n");
       goto ld_prune_ret_INVALID_CMDLINE;
     }
 #endif
@@ -1528,9 +1528,9 @@ int32_t flipscan(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t m
   pheno_ct[1] = popcount_longs(founder_phenos[1], unfiltered_sample_ctl);
   if ((!pheno_ct[0]) || (!pheno_ct[1])) {
     if (popcount_longs(founder_info, unfiltered_sample_ctl)) {
-      logprint("Error: --flip-scan requires at least one case and one control, and only\nconsiders founders.\n");
+      logerrprint("Error: --flip-scan requires at least one case and one control, and only\nconsiders founders.\n");
     } else {
-      logprint("Error: --flip-scan requires founders.  (--make-founders may come in handy\nhere.)\n");
+      logerrprint("Error: --flip-scan requires founders.  (--make-founders may come in handy\nhere.)\n");
     }
     goto flipscan_ret_INVALID_CMDLINE;
   }
@@ -2201,7 +2201,7 @@ int32_t ld_report_matrix(pthread_t* threads, Ld_info* ldip, FILE* bedfile, uintp
   }
   idx1_block_size = thread_workload * thread_ct;
   if ((parallel_tot > 1) && (marker_ct < 2 * parallel_tot)) {
-    LOGPRINTF("Error: Too few variants in --r%s run for --parallel %u %u.\n", g_ld_is_r2? "2" : "", parallel_idx + 1, parallel_tot);
+    LOGERRPRINTF("Error: Too few variants in --r%s run for --parallel %u %u.\n", g_ld_is_r2? "2" : "", parallel_idx + 1, parallel_tot);
     goto ld_report_matrix_ret_INVALID_CMDLINE;
   }
   if (!is_square) {
@@ -5675,7 +5675,7 @@ int32_t ld_report_regular(pthread_t* threads, Ld_info* ldip, FILE* bedfile, uint
           if (ii != -1) {
             uii = id_map[(uint32_t)ii];
             if (!is_set(marker_exclude_idx1, uii)) {
-	      logprint("Error: Duplicate variant ID in --ld-snp-list file.\n");
+	      logerrprint("Error: Duplicate variant ID in --ld-snp-list file.\n");
 	      goto ld_report_regular_ret_INVALID_FORMAT;
 	    }
             clear_bit(marker_exclude_idx1, uii);
@@ -5694,7 +5694,7 @@ int32_t ld_report_regular(pthread_t* threads, Ld_info* ldip, FILE* bedfile, uint
     }
   }
   if ((parallel_tot > 1) && (marker_ct1 < 2 * parallel_tot)) {
-    LOGPRINTF("Error: Too few variants in --r%s run for --parallel %u %u.\n", g_ld_is_r2? "2" : "", parallel_idx + 1, parallel_tot);
+    LOGERRPRINTF("Error: Too few variants in --r%s run for --parallel %u %u.\n", g_ld_is_r2? "2" : "", parallel_idx + 1, parallel_tot);
     goto ld_report_regular_ret_INVALID_CMDLINE;
   }
   // yeah, this is uneven in the inter-chr case
@@ -6027,7 +6027,7 @@ int32_t ld_report_regular(pthread_t* threads, Ld_info* ldip, FILE* bedfile, uint
     retval = RET_READ_FAIL;
     break;
   ld_report_regular_ret_EMPTY_SET1:
-    logprint("Error: No valid variants specified by --ld-snp/--ld-snps/--ld-snp-list.\n");
+    logerrprint("Error: No valid variants specified by --ld-snp/--ld-snps/--ld-snp-list.\n");
   ld_report_regular_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -6076,14 +6076,14 @@ int32_t ld_report(pthread_t* threads, Ld_info* ldip, FILE* bedfile, uintptr_t be
   g_ld_thread_ct = g_thread_ct;
   g_ld_set_allele_freqs = (ld_modifier & LD_WITH_FREQS)? set_allele_freqs : NULL;
   if (founder_ct < 2) {
-    LOGPRINTF("Warning: Skipping --r%s since there are less than two founders.\n(--make-founders may come in handy here.)\n", g_ld_is_r2? "2" : "");
+    LOGERRPRINTF("Warning: Skipping --r%s since there are less than two founders.\n(--make-founders may come in handy here.)\n", g_ld_is_r2? "2" : "");
     goto ld_report_ret_1;
   } else if (founder_ct >= 0x20000000) {
-    logprint("Error: --r/--r2 does not support >= 2^29 samples.\n");
+    logerrprint("Error: --r/--r2 does not support >= 2^29 samples.\n");
     goto ld_report_ret_INVALID_CMDLINE;
   }
   if ((marker_ct > 400000) && (!(ld_modifier & LD_YES_REALLY)) && (parallel_tot == 1) && ((ld_modifier & LD_MATRIX_SHAPEMASK) || ((ld_modifier & LD_INTER_CHR) && (!ldip->snpstr) && (!ldip->snps_rl.name_ct) && ((!g_ld_is_r2) || (ldip->window_r2 == 0.0))))) {
-    logprint("Error: Gigantic (over 400k loci) --r/--r2 unfiltered, non-distributed\ncomputation.  Rerun with the 'yes-really' modifier if you are SURE you have\nenough hard drive space and want to do this.\n");
+    logerrprint("Error: Gigantic (over 400k loci) --r/--r2 unfiltered, non-distributed\ncomputation.  Rerun with the 'yes-really' modifier if you are SURE you have\nenough hard drive space and want to do this.\n");
     goto ld_report_ret_INVALID_CMDLINE;
   }
   if (alloc_collapsed_haploid_filters(unfiltered_sample_ct, founder_ct, XMHH_EXISTS | hh_exists, 1, founder_info, sex_male, &founder_include2, &founder_male_include2)) {
@@ -6112,7 +6112,7 @@ int32_t ld_report(pthread_t* threads, Ld_info* ldip, FILE* bedfile, uintptr_t be
   *bufptr = '\0';
   if (ld_modifier & LD_INPHASE) {
     if (max_marker_allele_len * 4 + plink_maxsnp * 2 + get_max_chrom_len(chrom_info_ptr) * 2 + 128 > MAXLINELEN) {
-      logprint("Error: --r/--r2 in-phase does not support very long allele codes.\n");
+      logerrprint("Error: --r/--r2 in-phase does not support very long allele codes.\n");
       goto ld_report_ret_INVALID_CMDLINE;
     }
     g_ld_marker_allele_ptrs = marker_allele_ptrs;
@@ -6232,7 +6232,7 @@ int32_t show_tags(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t
   uint32_t uii;
   int32_t ii;
   if (founder_ct < 2) {
-    logprint("Warning: Skipping --show-tags since there are less than two founders.\n(--make-founders may come in handy here.)\n");
+    logerrprint("Warning: Skipping --show-tags since there are less than two founders.\n(--make-founders may come in handy here.)\n");
     goto show_tags_ret_1;
   }
   if (wkspace_alloc_ul_checked(&targets, unfiltered_marker_ctl * sizeof(intptr_t)) ||
@@ -6255,7 +6255,7 @@ int32_t show_tags(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t
     while (fgets(tbuf, MAXLINELEN, infile)) {
       line_idx++;
       if (!tbuf[MAXLINELEN - 1]) {
-	LOGPRINTF("Error: Line %" PRIuPTR " of --show-tags file is pathologically long.\n", line_idx);
+	LOGERRPRINTF("Error: Line %" PRIuPTR " of --show-tags file is pathologically long.\n", line_idx);
 	goto show_tags_ret_INVALID_FORMAT;
       }
       bufptr = skip_initial_spaces(tbuf);
@@ -6266,7 +6266,7 @@ int32_t show_tags(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t
       if (twocolumn) {
 	bufptr2 = skip_initial_spaces(&(bufptr[slen]));
         if (!bufptr2) {
-	  LOGPRINTF("Error: Line %" PRIuPTR " of --show-tags file has fewer tokens than expected.\n", line_idx);
+	  LOGERRPRINTF("Error: Line %" PRIuPTR " of --show-tags file has fewer tokens than expected.\n", line_idx);
 	  goto show_tags_ret_INVALID_FORMAT;
 	}
         if ((*bufptr2 != '1') || (!is_space_or_eoln(bufptr2[1]))) {
@@ -6281,7 +6281,7 @@ int32_t show_tags(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t
       marker_uidx = marker_id_map[(uint32_t)ii];
       if (IS_SET(targets, marker_uidx)) {
         bufptr[slen] = '\0';
-        LOGPRINTF("Error: Duplicate variant ID '%s' in --show-tags file.\n", bufptr);
+        LOGERRPRINTF("Error: Duplicate variant ID '%s' in --show-tags file.\n", bufptr);
 	goto show_tags_ret_INVALID_FORMAT;
       }
       SET_BIT(targets, marker_uidx);
@@ -6292,7 +6292,7 @@ int32_t show_tags(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t
     wkspace_reset((unsigned char*)marker_id_map);
     target_ct = popcount_longs(targets, unfiltered_marker_ctl);
     if (!target_ct) {
-      logprint("Error: No recognized variant IDs in --show-tags file.\n");
+      logerrprint("Error: No recognized variant IDs in --show-tags file.\n");
       goto show_tags_ret_INVALID_FORMAT;
     }
     if (wkspace_alloc_ul_checked(&final_set, unfiltered_marker_ctl * sizeof(intptr_t))) {
@@ -6301,7 +6301,7 @@ int32_t show_tags(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uintptr_t
     memcpy(final_set, targets, unfiltered_marker_ctl * sizeof(intptr_t));
     LOGPRINTF("--show-tags: %u target variant%s loaded.\n", target_ct, (target_ct == 1)? "" : "s");
     if (unrecog_ct) {
-      LOGPRINTF("Warning: %" PRIuPTR " unrecognized variant ID%s in --show-tags file.\n", unrecog_ct, (unrecog_ct == 1)? "" : "s");
+      LOGERRPRINTF("Warning: %" PRIuPTR " unrecognized variant ID%s in --show-tags file.\n", unrecog_ct, (unrecog_ct == 1)? "" : "s");
     }
   } else {
     bitfield_exclude_to_include(marker_exclude, targets, unfiltered_marker_ct);
@@ -7043,9 +7043,9 @@ int32_t haploview_blocks(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uin
   founder_ct = popcount_longs(founder_pnm, unfiltered_sample_ctl);
   if (founder_ct < 2) {
     if ((!no_pheno_req) && (!popcount_longs(pheno_nm, unfiltered_sample_ctl))) {
-      logprint("Warning: Skipping --blocks, since there are less than two founders with\nnonmissing phenotypes.  (The 'no-pheno-req' modifier removes the phenotype\nrestriction.)\n");
+      logerrprint("Warning: Skipping --blocks, since there are less than two founders with\nnonmissing phenotypes.  (The 'no-pheno-req' modifier removes the phenotype\nrestriction.)\n");
     } else {
-      logprint("Warning: Skipping --blocks, since there are less than two founders with\nnonmissing phenotypes.  (--make-founders may come in handy here.)\n");
+      logerrprint("Warning: Skipping --blocks, since there are less than two founders with\nnonmissing phenotypes.  (--make-founders may come in handy here.)\n");
     }
     goto haploview_blocks_ret_1;
   }
@@ -7064,7 +7064,7 @@ int32_t haploview_blocks(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uin
     marker_ct = unfiltered_marker_ct - popcount_longs(marker_exclude, unfiltered_marker_ctl);
   }
   if (marker_ct < 2) {
-    logprint("Warning: Skipping --blocks since there are too few variants with MAF >= 0.05.\n");
+    logerrprint("Warning: Skipping --blocks since there are too few variants with MAF >= 0.05.\n");
     goto haploview_blocks_ret_1;
   }
   pct_thresh = marker_ct / 100;
@@ -7107,7 +7107,8 @@ int32_t haploview_blocks(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uin
     }
 #ifndef __LP64__
     if (max_block_size > 65536) {
-      logprint("\nError: 32-bit --blocks cannot analyze potential blocks with more than 65536\nvariants.  Use a 64-bit PLINK build or a smaller --blocks-window-kb value.\n");
+      logprint("\n");
+      logerrprint("Error: 32-bit --blocks cannot analyze potential blocks with more than 65536\nvariants.  Use a 64-bit PLINK build or a smaller --blocks-window-kb value.\n");
       goto haploview_blocks_ret_INVALID_CMDLINE;
     }
 #endif
@@ -7770,7 +7771,7 @@ int32_t twolocus(Epi_info* epi_ip, FILE* bedfile, uintptr_t bed_offset, uintptr_
     // ulkk = (unfiltered_sample_ctl2 + 1) / 2;
     sample_ct = popcount_longs(sample_exclude, ulkk);
     if (!sample_ct) {
-      logprint("Warning: Skipping --ld since there are no founders.  (--make-founders may come\nin handy here.)\n");
+      logerrprint("Warning: Skipping --ld since there are no founders.  (--make-founders may come\nin handy here.)\n");
       goto twolocus_ret_1;
     }
     if (wkspace_alloc_ul_checked(&loadbuf_raw, ulkk * sizeof(intptr_t))) {
@@ -7925,7 +7926,7 @@ int32_t twolocus(Epi_info* epi_ip, FILE* bedfile, uintptr_t bed_offset, uintptr_
     counts_cc[7] = counts_all[3] + counts_all[11] + counts_all[15];
     count_total = counts_cc[0] + counts_cc[2] + counts_cc[3];
     if (!count_total) {
-      logprint("Error: No valid observations for --ld.\n");
+      logerrprint("Error: No valid observations for --ld.\n");
       goto twolocus_ret_INVALID_CMDLINE;
     }
     if ((!counts_cc[2]) && ((!counts_cc[0]) || (!counts_cc[3]))) {
@@ -8134,14 +8135,14 @@ int32_t twolocus(Epi_info* epi_ip, FILE* bedfile, uintptr_t bed_offset, uintptr_
     break;
   twolocus_ret_MARKER_NOT_FOUND:
     if (outname) {
-      logprint("Error: --twolocus variant name not found.\n");
+      logerrprint("Error: --twolocus variant name not found.\n");
     } else {
-      logprint("Error: --ld variant name not found.\n");
+      logerrprint("Error: --ld variant name not found.\n");
     }
     retval = RET_INVALID_CMDLINE;
     break;
   twolocus_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
   twolocus_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -8273,7 +8274,7 @@ int32_t epistasis_linear_regression(pthread_t* threads, Epi_info* epi_ip, FILE*
   // could add an epsilon here, but this is good enough to catch the most
   // common case (all phenotypes are the same integer near zero).
   if (g_epi_pheno_ssq * ((double)((int32_t)pheno_nm_ct)) == g_epi_pheno_sum * g_epi_pheno_sum) {
-    logprint("Error: Phenotype is constant.\n");
+    logerrprint("Error: Phenotype is constant.\n");
     goto epistasis_linear_regression_ret_INVALID_CMDLINE;
   }
   g_epi_vif_thresh = glm_vif_thresh;
@@ -9251,11 +9252,11 @@ int32_t epistasis_report(pthread_t* threads, Epi_info* epi_ip, FILE* bedfile, ui
       goto epistasis_report_ret_INVALID_CMDLINE_2;
     } else if (!is_set_by_set) {
       if (sip->ct > 1) {
-	logprint("Error: --{fast-}epistasis set-by-all requires exactly one set.  (--set-names or\n--set-collapse-all may be handy here.\n");
+	logerrprint("Error: --{fast-}epistasis set-by-all requires exactly one set.  (--set-names or\n--set-collapse-all may be handy here.\n");
 	goto epistasis_report_ret_INVALID_CMDLINE;
       }
     } else if (sip->ct > 2) {
-      logprint("Error: --{fast-}epistasis set-by-set requires exactly one or two sets.\n(--set-names or --set-collapse-all may be handy here.)\n");
+      logerrprint("Error: --{fast-}epistasis set-by-set requires exactly one or two sets.\n(--set-names or --set-collapse-all may be handy here.)\n");
       goto epistasis_report_ret_INVALID_CMDLINE;
     }
     if (wkspace_alloc_ul_checked(&marker_exclude1, unfiltered_marker_ctl * sizeof(intptr_t))) {
@@ -9273,7 +9274,7 @@ int32_t epistasis_report(pthread_t* threads, Epi_info* epi_ip, FILE* bedfile, ui
   if (pheno_nm_ct >= 0x20000000) {
     // may as well document the existence of sub-2b overflow conditions even
     // though they'll never come up
-    logprint("Error: --{fast-}epistasis does not support >= 2^29 samples.\n");
+    logerrprint("Error: --{fast-}epistasis does not support >= 2^29 samples.\n");
     goto epistasis_report_ret_INVALID_CMDLINE;
   }
   if (!pheno_d) {
@@ -10178,21 +10179,21 @@ int32_t epistasis_report(pthread_t* threads, Epi_info* epi_ip, FILE* bedfile, ui
   epistasis_report_ret_TOO_FEW_MARKERS:
     if (pheno_d) {
       if (is_triangular) {
-        logprint("Error: --epistasis requires 2+ non-monomorphic autosomal diploid loci.\n");
+        logerrprint("Error: --epistasis requires 2+ non-monomorphic autosomal diploid loci.\n");
       } else {
-        logprint("Error: Each --epistasis set must contain at least one non-monomorphic autosomal\ndiploid site.\n");
+        logerrprint("Error: Each --epistasis set must contain at least one non-monomorphic autosomal\ndiploid site.\n");
       }
     } else {
       if (is_triangular) {
-        logprint("Error: --{fast-}epistasis requires 2+ autosomal diploid loci not monomorphic in\neither cases or controls.\n");
+        logerrprint("Error: --{fast-}epistasis requires 2+ autosomal diploid loci not monomorphic in\neither cases or controls.\n");
       } else {
-        logprint("Error: Each --{fast-}epistasis set must contain at least one autosomal diploid\nlocus not monomorphic in either cases or controls.\n");
+        logerrprint("Error: Each --{fast-}epistasis set must contain at least one autosomal diploid\nlocus not monomorphic in either cases or controls.\n");
       }
     }
     retval = RET_INVALID_CMDLINE;
     break;
   epistasis_report_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
   epistasis_report_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -10274,7 +10275,7 @@ int32_t indep_pairphase(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uint
   uint32_t pct;
   uint32_t uii;
   if (founder_ct < 2) {
-    logprint("Warning: Skipping --indep-pairphase since there are less than two founders.\n(--make-founders may come in handy here.)\n");
+    logerrprint("Warning: Skipping --indep-pairphase since there are less than two founders.\n(--make-founders may come in handy here.)\n");
     goto indep_pairphase_ret_1;
   }
   if (is_set(chrom_info_ptr->chrom_mask, 0)) {
@@ -10289,7 +10290,7 @@ int32_t indep_pairphase(Ld_info* ldip, FILE* bedfile, uintptr_t bed_offset, uint
     LOGPRINTF("--indep-pairphase: Ignoring %" PRIuPTR " chromosome 0 variant%s.\n", ulii, (ulii == 1)? "" : "s");
   }
   if (marker_ct < 2) {
-    logprint("Error: Too few variants for --indep-pairphase.\n");
+    logerrprint("Error: Too few variants for --indep-pairphase.\n");
     goto indep_pairphase_ret_INVALID_FORMAT;
   }
 
@@ -10711,7 +10712,7 @@ int32_t epi_summary_merge(Epi_info* epi_ip, char* outname, char* outname_end) {
   }
   bufptr = &(inprefix[ulii - 2]);
   if (memcmp(".epi.", &(inprefix[ulii - 7]), 5) || (memcmp("cc", bufptr, 2) && memcmp("co", bufptr, 2) && memcmp("qt", bufptr, 2))) {
-    LOGPRINTFWW("Error: Invalid --epistasis-summary-merge filename prefix '%s'. (*.epi.cc, *.epi.co, or *.epi.qt expected.)\n", inprefix);
+    LOGERRPRINTFWW("Error: Invalid --epistasis-summary-merge filename prefix '%s'. (*.epi.cc, *.epi.co, or *.epi.qt expected.)\n", inprefix);
     goto epi_summary_merge_ret_INVALID_CMDLINE;
   }
   inprefix_end = memcpya(inprefix_end, ".summary.", 9);
@@ -11008,33 +11009,33 @@ int32_t epi_summary_merge(Epi_info* epi_ip, char* outname, char* outname_end) {
     retval = RET_INVALID_CMDLINE;
     break;
   epi_summary_merge_ret_MISMATCH:
-    logprint("Error: --epistasis-summary-merge files were generated from different datasets\nand/or settings.\n");
+    logerrprint("Error: --epistasis-summary-merge files were generated from different datasets\nand/or settings.\n");
     retval = RET_INVALID_FORMAT;
     break;
   epi_summary_merge_ret_INVALID_NSIG:
-    LOGPRINTFWW("Error: Invalid N_SIG value on line %" PRIuPTR " of %s .\n", line_idx, inprefix);
+    LOGERRPRINTFWW("Error: Invalid N_SIG value on line %" PRIuPTR " of %s .\n", line_idx, inprefix);
     retval = RET_INVALID_FORMAT;
     break;
   epi_summary_merge_ret_INVALID_NTOT:
-    LOGPRINTFWW("Error: Invalid N_SIG value on line %" PRIuPTR " of %s .\n", line_idx, inprefix);
+    LOGERRPRINTFWW("Error: Invalid N_SIG value on line %" PRIuPTR " of %s .\n", line_idx, inprefix);
     retval = RET_INVALID_FORMAT;
     break;
   epi_summary_merge_ret_INVALID_CHISQ:
-    LOGPRINTFWW("Error: Invalid BEST_CHISQ value on line %" PRIuPTR " of %s .\n", line_idx, inprefix);
+    LOGERRPRINTFWW("Error: Invalid BEST_CHISQ value on line %" PRIuPTR " of %s .\n", line_idx, inprefix);
     retval = RET_INVALID_FORMAT;
     break;
   epi_summary_merge_ret_MISSING_TOKENS:
-    LOGPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, inprefix);
+    LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s has fewer tokens than expected.\n", line_idx, inprefix);
     retval = RET_INVALID_FORMAT;
     break;
   epi_summary_merge_ret_LONG_LINE:
-    LOGPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, inprefix);
+    LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, inprefix);
     retval = RET_INVALID_FORMAT;
     break;
   epi_summary_merge_ret_INVALID_HEADER:
     LOGPREPRINTFWW(logbuf, "Error: Invalid --epistasis-summary-merge header in %s.\n", inprefix);
   epi_summary_merge_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -11188,11 +11189,11 @@ int32_t test_mishap(FILE* bedfile, uintptr_t bed_offset, char* outname, char* ou
   uint32_t ukk;
   uint32_t umm;
   if (is_set(chrom_info_ptr->haploid_mask, 1)) {
-    logprint("Error: --test-mishap can only be used on diploid genomes.\n");
+    logerrprint("Error: --test-mishap can only be used on diploid genomes.\n");
     goto test_mishap_ret_INVALID_CMDLINE;
   }
   if (sample_ct >= 0x40000000) {
-    logprint("Error: --test-mishap does not support >= 2^30 samples.\n");
+    logerrprint("Error: --test-mishap does not support >= 2^30 samples.\n");
     goto test_mishap_ret_INVALID_CMDLINE;
   }
   if (wkspace_alloc_ul_checked(&loadbuf_raw, unfiltered_sample_ctl2 * sizeof(intptr_t)) ||
@@ -11682,7 +11683,7 @@ int32_t construct_ld_map(pthread_t* threads, FILE* bedfile, uintptr_t bed_offset
   uint32_t range_start;
   uint32_t uii;
   if (!founder_ct) {
-    logprint("Error: Cannot construct LD map, since there are no founders with nonmissing\nphenotypes.  (--make-founders may come in handy here.)\n");
+    logerrprint("Error: Cannot construct LD map, since there are no founders with nonmissing\nphenotypes.  (--make-founders may come in handy here.)\n");
     goto construct_ld_map_ret_INVALID_CMDLINE;
   }
   ld_map = (uint32_t**)wkspace_alloc(marker_ct * sizeof(intptr_t));
@@ -12203,7 +12204,7 @@ int32_t set_test_common_init(pthread_t* threads, FILE* bedfile, uintptr_t bed_of
     }
   }
   if (!set_ct) {
-    logprint("Warning: No significant variants in any set.  Skipping permutation-based set\ntest.\n");
+    logerrprint("Warning: No significant variants in any set.  Skipping permutation-based set\ntest.\n");
     goto set_test_common_init_ret_1;
   }
   LOGPRINTFWW("%s set test: Testing %" PRIuPTR " set%s with at least one significant variant.\n", flag_descrip, set_ct, (set_ct == 1)? "" : "s");
@@ -12461,7 +12462,7 @@ void update_clump_histo(double pval, uintptr_t* histo) {
 
 int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outname_end, uintptr_t unfiltered_marker_ct, uintptr_t* marker_exclude, uintptr_t marker_ct, char* marker_ids, uintptr_t max_marker_id_len, uint32_t plink_maxsnp, uint32_t* marker_pos, char** marker_allele_ptrs, uintptr_t* marker_reverse, Chrom_info* chrom_info_ptr, uintptr_t unfiltered_sample_ct, uintptr_t* founder_info, Clump_info* clump_ip, uintptr_t* sex_male, uint32_t hh_exists) {
   unsigned char* wkspace_mark = wkspace_base;
-  FILE* infile = NULL;
+  gzFile gz_infile = NULL;
   FILE* outfile = NULL;
   FILE* outfile_ranges = NULL;
   FILE* outfile_best = NULL;
@@ -12607,11 +12608,11 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
   index_tots[4] = 0;
 
   if (annot_flattened && (!clump_verbose) && (!clump_best)) {
-    logprint("Error: --clump-annotate must be used with --clump-verbose or --clump-best.\n");
+    logerrprint("Error: --clump-annotate must be used with --clump-verbose or --clump-best.\n");
     goto clump_reports_ret_INVALID_CMDLINE;
   }
   if (!founder_ct) {
-    logprint("Warning: Skipping --clump since there are no founders.  (--make-founders may\ncome in handy here.)\n");
+    logerrprint("Warning: Skipping --clump since there are no founders.  (--make-founders may\ncome in handy here.)\n");
     goto clump_reports_ret_1;
   }
   if (clump_best) {
@@ -12716,7 +12717,7 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
     goto clump_reports_ret_NOMEM;
   }
   if (scan_for_duplicate_ids(sorted_header_dict, header_dict_ct, max_header_len)) {
-    logprint("Error: Duplicate --clump-snp-field/--clump-field/--clump-annotate field name.\n");
+    logerrprint("Error: Duplicate --clump-snp-field/--clump-field/--clump-annotate field name.\n");
     goto clump_reports_ret_INVALID_CMDLINE;
   }
 
@@ -12744,11 +12745,11 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
   if (clump_best) {
     if (file_ct == 2) {
       if (!clump_index_first) {
-        logprint("Error: --clump-best can no longer be used with two --clump files unless\n--clump-index-first is also specified.  (Contact the developers if this is\nproblematic.)\n");
+        logerrprint("Error: --clump-best can no longer be used with two --clump files unless\n--clump-index-first is also specified.  (Contact the developers if this is\nproblematic.)\n");
         goto clump_reports_ret_INVALID_CMDLINE;
       }
     } else if (file_ct > 2) {
-      logprint("Error: --clump-best can no longer be used with more than two --clump files.\n(Contact the developers if this is problematic.)\n");
+      logerrprint("Error: --clump-best can no longer be used with more than two --clump files.\n(Contact the developers if this is problematic.)\n");
       goto clump_reports_ret_INVALID_CMDLINE;
     }
     // only draw proxies from this file
@@ -12771,9 +12772,12 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
   }
   // load in reverse order since we're adding to the front of the linked lists
   for (file_idx = file_ct; file_idx; file_idx--) {
-    if (fopen_checked(&infile, fname_ptr, "r")) {
+    if (gzopen_checked(&gz_infile, fname_ptr, "rb")) {
       goto clump_reports_ret_OPEN_FAIL;
     }
+    if (gzbuffer(gz_infile, 131072)) {
+      goto clump_reports_ret_NOMEM;
+    }
     loadbuft_size = wkspace_left - topsize;
     if (loadbuft_size <= 2 * MAXLINELEN + extra_annot_space) {
       goto clump_reports_ret_NOMEM2;
@@ -12787,9 +12791,31 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
     }
     ukk = 0x7fffffff; // highest-precedence variant ID header seen so far
     umm = 0x7fffffff; // highest-precedence p-value header seen so far
-    retval = load_to_first_token(infile, loadbuft_size, '\0', "--clump", loadbuft, &bufptr, &line_idx);
-    if (retval) {
-      goto clump_reports_ret_1;
+    // load_to_first_token() with potentially gzipped input.  Move this to
+    // plink_common if anything else needs it.
+    line_idx = 0;
+    while (1) {
+      line_idx++;
+      if (!gzgets(gz_infile, loadbuft, loadbuft_size)) {
+	if (!gzeof(gz_infile)) {
+	  goto clump_reports_ret_READ_FAIL;
+	} else {
+          LOGPREPRINTFWW("Error: Empty %s.\n", fname_ptr);
+	  goto clump_reports_ret_INVALID_FORMAT_2;
+	}
+      }
+      if (!(loadbuft[loadbuft_size - 1])) {
+	if (loadbuft_size == MAXLINEBUFLEN / 2) {
+	  LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, fname_ptr);
+	  goto clump_reports_ret_INVALID_FORMAT_2;
+	} else {
+	  goto clump_reports_ret_NOMEM;
+	}
+      }
+      bufptr = skip_initial_spaces(loadbuft);
+      if (!is_eoln_kns(*bufptr)) {
+	break;
+      }
     }
     fill_ulong_zero(col_bitfield, annot_ct_p2_ctl);
     uii = 0; // current 0-based column number
@@ -12852,8 +12878,14 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
       parse_table[uii * 2 + 1] -= parse_table[uii * 2 - 1] + 1;
     }
   clump_reports_load_loop:
-    while (fgets(loadbuft, loadbuft_size, infile)) {
+    while (1) {
       line_idx++;
+      if (!gzgets(gz_infile, loadbuft, loadbuft_size)) {
+	if (!gzeof(gz_infile)) {
+	  goto clump_reports_ret_READ_FAIL;
+	}
+	break;
+      }
       if (!loadbuft[loadbuft_size - 1]) {
 	if (loadbuft_size == MAXLINEBUFLEN / 2) {
 	  LOGPREPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, fname_ptr);
@@ -12956,7 +12988,7 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
 	loadbuft[loadbuft_size - 1] = ' ';
       }
     }
-    if (fclose_null(&infile)) {
+    if (gzclose_null(&gz_infile)) {
       goto clump_reports_ret_READ_FAIL;
     }
     if (file_idx > 1) {
@@ -12973,7 +13005,7 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
   // 4. sort p-val array, greedily form clumps
   index_ct = popcount_longs(cur_bitfield, marker_ctl);
   if (!index_ct) {
-    logprint("Warning: No significant --clump results.  Skipping.\n");
+    logerrprint("Warning: No significant --clump results.  Skipping.\n");
     goto clump_reports_ret_1;
   }
   wkspace_left -= topsize;
@@ -13812,10 +13844,10 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
     } else {
       uljj = MINV(missing_variant_ct, 3);
       for (ulii = 0; ulii < uljj; ulii++) {
-	LOGPRINTFWW("Warning: '%s' is missing from the main dataset, and is a top variant.\n", &(sorted_missing_variant_ids[ulii * max_missing_id_len]));
+	LOGERRPRINTFWW("Warning: '%s' is missing from the main dataset, and is a top variant.\n", &(sorted_missing_variant_ids[ulii * max_missing_id_len]));
       }
       if (missing_variant_ct > 3) {
-        printf("%" PRIuPTR " more top variant ID%s missing; see log file.\n", missing_variant_ct - 3, (missing_variant_ct == 4)? "" : "s");
+        fprintf(stderr, "%" PRIuPTR " more top variant ID%s missing; see log file.\n", missing_variant_ct - 3, (missing_variant_ct == 4)? "" : "s");
 	for (ulii = 3; ulii < missing_variant_ct; ulii++) {
 	  LOGPREPRINTFWW("Warning: '%s' is missing from the main dataset, and is a top variant.\n", &(sorted_missing_variant_ids[ulii * max_missing_id_len]));
 	  logstr(logbuf);
@@ -13860,13 +13892,13 @@ int32_t clump_reports(FILE* bedfile, uintptr_t bed_offset, char* outname, char*
     *bufptr2 = '\0';
     LOGPREPRINTFWW("Error: Duplicate column header '%s' in %s.\n", bufptr, fname_ptr);
   clump_reports_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
  clump_reports_ret_1:
   wkspace_reset(wkspace_mark);
-  fclose_cond(infile);
+  gzclose_cond(gz_infile);
   fclose_cond(outfile);
   fclose_cond(outfile_ranges);
   fclose_cond(outfile_best);
diff --git a/plink_misc.c b/plink_misc.c
index 14eb7e2..20d9791 100644
--- a/plink_misc.c
+++ b/plink_misc.c
@@ -210,7 +210,7 @@ int32_t makepheno_load(FILE* phenofile, char* makepheno_str, uintptr_t unfiltere
     retval = RET_READ_FAIL;
     break;
   makepheno_load_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -293,7 +293,7 @@ int32_t load_pheno(FILE* phenofile, uintptr_t unfiltered_sample_ct, uintptr_t sa
 	  do {
 	    bufptr = next_token(bufptr);
 	    if (no_more_tokens_kns(bufptr)) {
-	      logprint("Error: --pheno-name column not found.\n");
+	      logerrprint("Error: --pheno-name column not found.\n");
               goto load_pheno_ret_INVALID_FORMAT;
 	    }
 	    mpheno_col++;
@@ -306,13 +306,13 @@ int32_t load_pheno(FILE* phenofile, uintptr_t unfiltered_sample_ct, uintptr_t sa
 	  }
 	  tmp_len = strlen_se(bufptr);
 	  if (tmp_len > max_pheno_name_len) {
-	    logprint("Error: Excessively long phenotype name in --pheno file.\n");
+	    logerrprint("Error: Excessively long phenotype name in --pheno file.\n");
             goto load_pheno_ret_INVALID_FORMAT;
 	  }
           memcpyx(phenoname_load, bufptr, tmp_len, '\0');
 	}
       } else if (phenoname_str) {
-	logprint("Error: --pheno-name requires the --pheno file to have a header line with first\ntwo columns 'FID' and 'IID'.\n");
+	logerrprint("Error: --pheno-name requires the --pheno file to have a header line with first\ntwo columns 'FID' and 'IID'.\n");
 	goto load_pheno_ret_INVALID_FORMAT;
       } else {
 	header_processed = 1;
@@ -400,7 +400,7 @@ int32_t load_pheno(FILE* phenofile, uintptr_t unfiltered_sample_ct, uintptr_t sa
   load_pheno_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --pheno file has fewer tokens than expected.\n", line_idx);
   load_pheno_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_pheno_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -416,7 +416,7 @@ int32_t convert_tail_pheno(uint32_t unfiltered_sample_ct, uintptr_t* pheno_nm, u
   uint32_t sample_uidx_stop;
   double dxx;
   if (!(*pheno_d_ptr)) {
-    logprint("Error: --tail-pheno requires scalar phenotype data.\n");
+    logerrprint("Error: --tail-pheno requires scalar phenotype data.\n");
     return RET_INVALID_FORMAT;
   }
   sample_uidx = (unfiltered_sample_ct + (BITCT - 1)) / BITCT;
@@ -505,7 +505,7 @@ int32_t apply_cm_map(char* cm_map_fname, char* cm_map_chrname, uintptr_t unfilte
       bufptr = uint32_write(fname_write, uii);
       memcpy(bufptr, at_sign_ptr, post_at_sign_len);
       if (fopen_checked(&shapeitfile, fname_buf, "r")) {
-	LOGPRINTFWW("Warning: --cm-map failed to open %s.\n", fname_buf);
+	LOGERRPRINTFWW("Warning: --cm-map failed to open %s.\n", fname_buf);
         continue;
       }
     } else {
@@ -555,7 +555,7 @@ int32_t apply_cm_map(char* cm_map_fname, char* cm_map_chrname, uintptr_t unfilte
         goto apply_cm_map_ret_INVALID_FORMAT_2;
       }
       if (bp_new <= bp_old) {
-        logprint("Error: bp coordinates in --cm-map file are not in increasing order.\n");
+        logerrprint("Error: bp coordinates in --cm-map file are not in increasing order.\n");
         goto apply_cm_map_ret_INVALID_FORMAT;
       }
       bufptr2 = next_token_mult(bufptr, 2);
@@ -595,7 +595,7 @@ int32_t apply_cm_map(char* cm_map_fname, char* cm_map_chrname, uintptr_t unfilte
       goto apply_cm_map_ret_READ_FAIL;
     }
     if (irreg_line_ct) {
-      LOGPRINTFWW("Warning: %" PRIuPTR " irregular line%s skipped in %s.\n", irreg_line_ct, (irreg_line_ct == 1)? "" : "s", fname_buf);
+      LOGERRPRINTFWW("Warning: %" PRIuPTR " irregular line%s skipped in %s.\n", irreg_line_ct, (irreg_line_ct == 1)? "" : "s", fname_buf);
     }
   }
   LOGPRINTF("--cm-map: %u chromosome%s updated.\n", updated_chrom_ct, (updated_chrom_ct == 1)? "" : "s");
@@ -607,13 +607,13 @@ int32_t apply_cm_map(char* cm_map_fname, char* cm_map_chrname, uintptr_t unfilte
     retval = RET_READ_FAIL;
     break;
   apply_cm_map_ret_INVALID_CMDLINE_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_CMDLINE;
     break;
   apply_cm_map_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --cm-map file has fewer tokens than expected.\n", line_idx);
   apply_cm_map_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   apply_cm_map_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -733,7 +733,7 @@ int32_t update_marker_cms(Two_col_params* update_cm, uint32_t* marker_id_htable,
   update_marker_cms_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --update-cm file has fewer tokens than expected.\n", line_idx);
   update_marker_cms_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -858,7 +858,7 @@ int32_t update_marker_pos(Two_col_params* update_map, uint32_t* marker_id_htable
   logprintb();
   *marker_exclude_ct_ptr = unfiltered_marker_ct - marker_ct;
   if (!marker_ct) {
-    logprint("Error: All variants excluded by --update-map (due to negative marker\npositions).\n");
+    logerrprint("Error: All variants excluded by --update-map (due to negative marker\npositions).\n");
     goto update_marker_pos_ret_ALL_MARKERS_EXCLUDED;
   }
   for (marker_uidx = 0, marker_idx = 0; marker_idx < marker_ct; marker_uidx++, marker_idx++) {
@@ -870,7 +870,7 @@ int32_t update_marker_pos(Two_col_params* update_map, uint32_t* marker_id_htable
     if (last_pos > marker_pos[marker_uidx]) {
       map_is_unsorted |= UNSORTED_BP;
       if (!((*map_is_unsorted_ptr) & UNSORTED_BP)) {
-	logprint("Warning: Base-pair positions are now unsorted!\n");
+	logerrprint("Warning: Base-pair positions are now unsorted!\n");
       }
       break;
     }
@@ -890,7 +890,7 @@ int32_t update_marker_pos(Two_col_params* update_map, uint32_t* marker_id_htable
   update_marker_pos_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --update-map file has fewer tokens than expected.\n", line_idx);
   update_marker_pos_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   update_marker_pos_ret_ALL_MARKERS_EXCLUDED:
@@ -999,7 +999,7 @@ int32_t update_marker_names(Two_col_params* update_name, uint32_t* marker_id_hta
     retval = RET_READ_FAIL;
     break;
   update_marker_names_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1169,9 +1169,9 @@ int32_t update_marker_alleles(char* update_alleles_fname, uint32_t* marker_id_ht
     retval = RET_WRITE_FAIL;
     break;
   update_marker_alleles_ret_DUPLICATE_ALLELE_CODE:
-    LOGPRINTF("Error: Duplicate allele code on line %" PRIuPTR " of --update-alleles file.\n", line_idx);
+    LOGERRPRINTF("Error: Duplicate allele code on line %" PRIuPTR " of --update-alleles file.\n", line_idx);
   update_marker_alleles_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1286,7 +1286,7 @@ int32_t flip_strand(char* flip_fname, uint32_t* marker_id_htable, uint32_t marke
       curtoklen = (uintptr_t)(bufptr2 - bufptr);
       if (bufptr2 == &(tbuf[MAXLINELEN * 2])) {
         if (curtoklen > MAX_ID_LEN) {
-	  logprint("Error: Excessively long ID in --flip file.\n");
+	  logerrprint("Error: Excessively long ID in --flip file.\n");
 	  goto flip_strand_ret_INVALID_FORMAT;
 	}
         bufptr3 = &(tbuf[MAXLINELEN - curtoklen]);
@@ -1309,7 +1309,7 @@ int32_t flip_strand(char* flip_fname, uint32_t* marker_id_htable, uint32_t marke
   }
   logprintb();
   if (non_acgt_ct) {
-    LOGPRINTF("Warning: %u variant%s had at least one non-A/C/G/T allele name.\n", non_acgt_ct, (non_acgt_ct == 1)? "" : "s");
+    LOGERRPRINTF("Warning: %u variant%s had at least one non-A/C/G/T allele name.\n", non_acgt_ct, (non_acgt_ct == 1)? "" : "s");
   }
   while (0) {
   flip_strand_ret_NOMEM:
@@ -1322,7 +1322,7 @@ int32_t flip_strand(char* flip_fname, uint32_t* marker_id_htable, uint32_t marke
     retval = RET_READ_FAIL;
     break;
   flip_strand_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   flip_strand_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -1416,7 +1416,7 @@ int32_t update_sample_ids(char* update_ids_fname, char* sorted_sample_ids, uintp
     retval = RET_READ_FAIL;
     break;
   update_sample_ids_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1519,7 +1519,7 @@ int32_t update_sample_parents(char* update_parents_fname, char* sorted_sample_id
     retval = RET_READ_FAIL;
     break;
   update_sample_parents_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1638,7 +1638,7 @@ int32_t update_sample_sexes(char* update_sex_fname, uint32_t update_sex_col, cha
   update_sample_sexes_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --update-sex file has fewer tokens than expected.\n", line_idx);
   update_sample_sexes_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -1886,7 +1886,7 @@ int32_t read_external_freqs(char* freqname, uintptr_t unfiltered_marker_ct, uint
   loadbuf[loadbuf_size - 1] = ' ';
   do {
     if (!fgets(loadbuf, loadbuf_size, freqfile)) {
-      logprint("Error: Empty --read-freq file.\n");
+      logerrprint("Error: Empty --read-freq file.\n");
       goto read_external_freqs_ret_INVALID_FORMAT;
     }
     line_idx++;
@@ -1897,7 +1897,7 @@ int32_t read_external_freqs(char* freqname, uintptr_t unfiltered_marker_ct, uint
   } while (is_eoln_kns(*bufptr));
   uii = get_freq_file_type(bufptr);
   if (!uii) {
-    logprint("Error: Invalid --read-freq file header.\n");
+    logerrprint("Error: Invalid --read-freq file header.\n");
     goto read_external_freqs_ret_INVALID_FORMAT;
   }
   if (uii < 3) {
@@ -2073,7 +2073,7 @@ int32_t read_external_freqs(char* freqname, uintptr_t unfiltered_marker_ct, uint
     // Also support GCTA-style frequency files:
     // [marker ID]\t[reference allele]\t[frequency of reference allele]\n
     if (nchrobs) {
-      logprint("Error: The current run requires an allele frequency file with observation\ncounts.\n");
+      logerrprint("Error: The current run requires an allele frequency file with observation\ncounts.\n");
       goto read_external_freqs_ret_INVALID_FORMAT;
     }
 
@@ -2132,7 +2132,7 @@ int32_t read_external_freqs(char* freqname, uintptr_t unfiltered_marker_ct, uint
   while (0) {
   read_external_freqs_ret_TOO_LONG_LINE:
     if (loadbuf_size == MAXLINEBUFLEN) {
-      LOGPRINTF("Error: Line %" PRIuPTR " of --read-freq file is pathologically long.\n", line_idx);
+      LOGERRPRINTF("Error: Line %" PRIuPTR " of --read-freq file is pathologically long.\n", line_idx);
       retval = RET_INVALID_FORMAT;
       break;
     }
@@ -2143,41 +2143,41 @@ int32_t read_external_freqs(char* freqname, uintptr_t unfiltered_marker_ct, uint
     retval = RET_OPEN_FAIL;
     break;
   read_external_freqs_ret_INVALID_HOM_A1:
-    LOGPRINTF("Error: Invalid hom. A1 count on line %" PRIuPTR " of --read-freq file.\n", line_idx);
+    LOGERRPRINTF("Error: Invalid hom. A1 count on line %" PRIuPTR " of --read-freq file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_INVALID_HOM_A2:
-    LOGPRINTF("Error: Invalid hom. A2 count on line %" PRIuPTR " of --read-freq file.\n", line_idx);
+    LOGERRPRINTF("Error: Invalid hom. A2 count on line %" PRIuPTR " of --read-freq file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_INVALID_NCHROBS:
-    LOGPRINTF("Error: Invalid NCHROBS value on line %" PRIuPTR " of --read-freq file.\n", line_idx);
+    LOGERRPRINTF("Error: Invalid NCHROBS value on line %" PRIuPTR " of --read-freq file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_INVALID_MAF:
-    LOGPRINTF("Error: Invalid MAF on line %" PRIuPTR " of --read-freq file.\n", line_idx);
+    LOGERRPRINTF("Error: Invalid MAF on line %" PRIuPTR " of --read-freq file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_A1_A2_SAME:
-    LOGPRINTF("Error: A1 and A2 alleles match on line %" PRIuPTR " of --read-freq file.\n", line_idx);
+    LOGERRPRINTF("Error: A1 and A2 alleles match on line %" PRIuPTR " of --read-freq file.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_INVALID_CHROM:
     sprintf(logbuf, "Error: Invalid chromosome code on line %" PRIuPTR" of --read-freq file.\n", line_idx);
   read_external_freqs_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   read_external_freqs_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_MISSING_TOKENS:
-    LOGPRINTF("Error: Line %" PRIuPTR " of --read-freq file has fewer tokens than expected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of --read-freq file has fewer tokens than expected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   read_external_freqs_ret_ALLELE_MISMATCH:
     if (retval == RET_NOMEM) {
       break;
     }
-    LOGPRINTF("Error: Allele(s) on line %" PRIuPTR " of --read-freq file don't match loaded\nvalues.\n", line_idx);
+    LOGERRPRINTF("Error: Allele(s) on line %" PRIuPTR " of --read-freq file don't match loaded\nvalues.\n", line_idx);
     break;
   }
  read_external_freqs_ret_1:
@@ -2304,7 +2304,7 @@ int32_t load_ax_alleles(Two_col_params* axalleles, uintptr_t unfiltered_marker_c
       }
     } else {
       colid_ptr[idlen] = '\0';
-      LOGPRINTF("Warning: Impossible A%c allele assignment for variant %s.\n", is_a2? '2' : '1', colid_ptr);
+      LOGERRPRINTF("Warning: Impossible A%c allele assignment for variant %s.\n", is_a2? '2' : '1', colid_ptr);
     }
   }
   if (!feof(infile)) {
@@ -2321,7 +2321,7 @@ int32_t load_ax_alleles(Two_col_params* axalleles, uintptr_t unfiltered_marker_c
     retval = RET_READ_FAIL;
     break;
   load_ax_alleles_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -3123,10 +3123,10 @@ int32_t sexcheck(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outna
 	}
       }
     } else if (yonly) {
-      logprint("Error: --check-sex/--impute-sex y-only requires Y chromosome data.\n");
+      logerrprint("Error: --check-sex/--impute-sex y-only requires Y chromosome data.\n");
       goto sexcheck_ret_INVALID_CMDLINE;
     } else {
-      LOGPRINTF("Warning: No Y chromosome data for --%s-sex ycount.\n", do_impute? "impute" : "check");
+      LOGERRPRINTF("Warning: No Y chromosome data for --%s-sex ycount.\n", do_impute? "impute" : "check");
     }
   }
   memcpy(outname_end, ".sexcheck", 10);
@@ -3250,7 +3250,7 @@ int32_t sexcheck(FILE* bedfile, uintptr_t bed_offset, char* outname, char* outna
     retval = RET_WRITE_FAIL;
     break;
   sexcheck_ret_NO_X_VAR:
-    logprint("Error: --check-sex/--impute-sex requires at least one polymorphic X chromosome\nlocus.\n");
+    logerrprint("Error: --check-sex/--impute-sex requires at least one polymorphic X chromosome\nlocus.\n");
   sexcheck_ret_INVALID_CMDLINE:
     retval = RET_INVALID_CMDLINE;
     break;
@@ -3339,7 +3339,7 @@ int32_t write_var_ranges(char* outname, char* outname_end, uintptr_t unfiltered_
   uintptr_t new_marker_idx;
   uint32_t block_idx;
   if (write_var_range_ct > marker_ct) {
-    logprint("Error: --write-var-ranges block count exceeds the total number of variants.\n");
+    logerrprint("Error: --write-var-ranges block count exceeds the total number of variants.\n");
     goto write_var_ranges_ret_INVALID_CMDLINE;
   }
   memcpy(outname_end, ".var.ranges", 12);
@@ -3624,7 +3624,7 @@ int32_t list_duplicate_vars(char* outname, char* outname_end, uint32_t dupvar_mo
       next_unset_ul_unsafe_ck(uniqueness_check_bitfield, &marker_uidx2);
       wptr = &(marker_ids[marker_uidx2 * max_marker_id_len]);
       if (id_htable_find(wptr, strlen(wptr), reported_id_htable, reported_id_htable_size, marker_ids, max_marker_id_len) != 0xffffffffU) {
-        LOGPRINTFWW("Error: Duplicate ID '%s'.\n", wptr);
+        LOGERRPRINTFWW("Error: Duplicate ID '%s'.\n", wptr);
 	goto list_duplicate_vars_ret_INVALID_FORMAT;
       }
     }
@@ -3710,7 +3710,7 @@ int32_t het_report(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
   uint32_t obs_ct;
   pzwrite_init_null(&ps);
   if (is_set(chrom_info_ptr->haploid_mask, 0)) {
-    logprint("Error: --het cannot be used on haploid genomes.\n");
+    logerrprint("Error: --het cannot be used on haploid genomes.\n");
     goto het_report_ret_INVALID_CMDLINE;
   }
   if (wkspace_alloc_uc_checked(&overflow_buf, PIGZ_BLOCK_SIZE + MAXLINELEN) ||
@@ -3882,7 +3882,7 @@ int32_t het_report(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
     retval = RET_WRITE_FAIL;
     break;
   het_report_ret_INVALID_CMDLINE:
-    logprint("Error: --het requires at least one polymorphic autosomal marker.\n");
+    logerrprint("Error: --het requires at least one polymorphic autosomal marker.\n");
     retval = RET_INVALID_CMDLINE;
     break;
   }
@@ -3952,7 +3952,7 @@ int32_t fst_report(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
   uint32_t ujj;
   marker_ct -= count_non_autosomal_markers(chrom_info_ptr, marker_exclude, 1, 1);
   if (!marker_ct) {
-    logprint("Error: No autosomal diploid variants for --fst.\n");
+    logerrprint("Error: No autosomal diploid variants for --fst.\n");
     goto fst_report_ret_INVALID_CMDLINE;
   }
   if (pheno_c) {
@@ -3969,7 +3969,7 @@ int32_t fst_report(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
     cur_sample_ct = popcount_longs(pheno_nm, unfiltered_sample_ctl);
     uii = popcount_longs(pheno_c, unfiltered_sample_ctl);
     if ((!uii) || (cur_sample_ct == uii)) {
-      logprint("Error: --fst case-control requires at least one case and one control.\n");
+      logerrprint("Error: --fst case-control requires at least one case and one control.\n");
       goto fst_report_ret_INVALID_CMDLINE;
     }
     cluster_sizes[0] = cur_sample_ct - uii;
@@ -3999,7 +3999,7 @@ int32_t fst_report(FILE* bedfile, uintptr_t bed_offset, char* outname, char* out
     }
     cluster_ct = uii;
     if (cluster_ct < 2) {
-      logprint("Error: --fst requires at least two nonempty clusters.\n");
+      logerrprint("Error: --fst requires at least two nonempty clusters.\n");
       goto fst_report_ret_INVALID_CMDLINE;
     }
     wkspace_shrink_top(cluster_sizes, cluster_ct * sizeof(int32_t));
@@ -4180,6 +4180,8 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
   uintptr_t final_mask = get_final_mask(sample_ct);
   uintptr_t topsize = 0;
   uintptr_t miss_ct = 0;
+  uint32_t miss_varid_ct = 0;
+  uint32_t miss_allele_ct = 0;
   uintptr_t range_ct = 0;
   uintptr_t range_skip = 0;
   uintptr_t ulii = 0;
@@ -4336,6 +4338,7 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
   if (modifier & SCORE_HEADER) {
     goto score_report_load_next;
   }
+  memcpy(outname_end, ".nopred", 8);
   while (1) {
     bufptr_arr[0] = next_token_multz(bufptr, first_col_m1);
     bufptr_arr[1] = next_token_mult(bufptr_arr[0], col_01_delta);
@@ -4349,6 +4352,16 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
       uii = strcmp(bufptr_arr[allele_idx], marker_allele_ptrs[2 * marker_uidx]);
       if ((!uii) || (!strcmp(bufptr_arr[allele_idx], marker_allele_ptrs[2 * marker_uidx + 1]))) {
         if (scan_double(bufptr_arr[effect_idx], &(dptr[marker_uidx]))) {
+	  if (!miss_ct) {
+	    if (fopen_checked(&outfile, outname, "w")) {
+	      goto score_report_ret_OPEN_FAIL;
+	    }
+	  }
+	  fputs("BADVAL\t", outfile);
+	  if (fwrite_checked(bufptr_arr[varid_idx], strlen_se(bufptr_arr[varid_idx]), outfile)) {
+	    goto score_report_ret_WRITE_FAIL;
+	  }
+	  putc('\n', outfile);
           miss_ct++;
 	} else {
 	  if (!IS_SET(marker_exclude, marker_uidx)) {
@@ -4362,10 +4375,38 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
 	  }
 	}
       } else {
+	if (!miss_ct) {
+	  if (fopen_checked(&outfile, outname, "w")) {
+	    goto score_report_ret_OPEN_FAIL;
+	  }
+	}
+	fputs("NOALLELE\t", outfile);
+	if (fwrite_checked(bufptr_arr[varid_idx], strlen_se(bufptr_arr[varid_idx]), outfile)) {
+	  goto score_report_ret_WRITE_FAIL;
+	}
+	putc(' ', outfile);
+	fputs(bufptr_arr[allele_idx], outfile);
+	fputs(" vs ", outfile);
+	fputs(marker_allele_ptrs[2 * marker_uidx], outfile);
+	putc(' ', outfile);
+	fputs(marker_allele_ptrs[2 * marker_uidx + 1], outfile);
+	putc('\n', outfile);
 	miss_ct++;
+	miss_allele_ct++;
       }
     } else {
+      if (!miss_ct) {
+	if (fopen_checked(&outfile, outname, "w")) {
+	  goto score_report_ret_OPEN_FAIL;
+	}
+      }
+      fputs("NOSNP\t", outfile);
+      if (fwrite_checked(bufptr_arr[varid_idx], strlen_se(bufptr_arr[varid_idx]), outfile)) {
+	goto score_report_ret_WRITE_FAIL;
+      }
+      putc('\n', outfile);
       miss_ct++;
+      miss_varid_ct++;
     }
   score_report_load_next:
     line_idx++;
@@ -4389,15 +4430,19 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
   }
   cur_marker_ct = unfiltered_marker_ct - popcount_longs(marker_exclude, unfiltered_marker_ctl);
   if (!cur_marker_ct) {
-    logprint("Error: No valid entries in --score file.\n");
+    logerrprint("Error: No valid entries in --score file.\n");
     goto score_report_ret_INVALID_FORMAT;
   } else if (cur_marker_ct >= 0x40000000) {
-    logprint("Error: --score does not support >= 2^30 variants.\n");
+    logerrprint("Error: --score does not support >= 2^30 variants.\n");
     goto score_report_ret_INVALID_FORMAT;
   }
   if (miss_ct) {
-    LOGPRINTF("Warning: %" PRIuPTR " line%s skipped in --score file.\n", miss_ct, (miss_ct == 1)? "" : "s");
+    LOGERRPRINTFWW("Warning: %" PRIuPTR " line%s skipped in --score file (%u due to variant ID mismatch, %u due to allele code mismatch); see %s for details.\n", miss_ct, (miss_ct == 1)? "" : "s", miss_varid_ct, miss_allele_ct, outname);
+    if (fclose_null(&outfile)) {
+      goto score_report_ret_WRITE_FAIL;
+    }
   }
+  LOGPRINTF("--score: %u valid predictor%s loaded.\n", cur_marker_ct, (cur_marker_ct == 1)? "" : "s");
   if (sc_ip->data_fname) {
     effect_sizes_cur = dptr; // not collapsed yet
     ulii = topsize;
@@ -4484,7 +4529,7 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
     }
     marker_ct = unfiltered_marker_ct - popcount_longs(marker_exclude_main, unfiltered_marker_ctl);
     if (!marker_ct) {
-      logprint("Error: No valid entries in --q-score-range data file.\n");
+      logerrprint("Error: No valid entries in --q-score-range data file.\n");
       goto score_report_ret_INVALID_FORMAT;
     }
     wkspace_left -= topsize;
@@ -4504,7 +4549,7 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
       goto score_report_ret_NOMEM;
     }
     if (miss_ct) {
-      LOGPRINTF("Warning: %" PRIuPTR " line%s skipped in --q-score-range data file.\n", miss_ct, (miss_ct == 1)? "" : "s");
+      LOGERRPRINTF("Warning: %" PRIuPTR " line%s skipped in --q-score-range data file.\n", miss_ct, (miss_ct == 1)? "" : "s");
     }
     miss_ct = 0;
     if (fopen_checked(&infile, sc_ip->range_fname, "r")) {
@@ -4839,13 +4884,13 @@ int32_t score_report(Score_info* sc_ip, FILE* bedfile, uintptr_t bed_offset, uin
     retval = RET_WRITE_FAIL;
     break;
   score_report_ret_MISSING_TOKENS_Q:
-    LOGPRINTF("Error: Line %" PRIuPTR " of --q-score-range data file has fewer tokens than\nexpected.\n", line_idx);
+    LOGERRPRINTF("Error: Line %" PRIuPTR " of --q-score-range data file has fewer tokens than\nexpected.\n", line_idx);
     retval = RET_INVALID_FORMAT;
     break;
   score_report_ret_MISSING_TOKENS:
     sprintf(logbuf, "Error: Line %" PRIuPTR " of --score file has fewer tokens than expected.\n", line_idx);
   score_report_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   score_report_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -5054,7 +5099,7 @@ int32_t meta_analysis_open_and_read_header(const char* fname, char* loadbuf, uin
     sprintf(logbuf, "Error: Duplicate column header '%s' in %s.\n", bufptr, fname);
   meta_analysis_open_and_read_header_ret_INVALID_FORMAT_WW:
     wordwrap(logbuf, 0);
-    logprintb();
+    logerrprintb();
     retval = RET_INVALID_FORMAT;
     break;
   }
@@ -5337,7 +5382,7 @@ int32_t meta_analysis(char* input_fnames, char* snpfield_search_order, char* a1f
     goto meta_analysis_ret_NOMEM;
   }
   if (scan_for_duplicate_ids(sorted_header_dict, header_dict_ct, max_header_len)) {
-    logprint("Error: Duplicate/invalid --meta-analysis-...-field field name.\n");
+    logerrprint("Error: Duplicate/invalid --meta-analysis-...-field field name.\n");
     goto meta_analysis_ret_INVALID_CMDLINE;
   }
 
@@ -5367,11 +5412,11 @@ int32_t meta_analysis(char* input_fnames, char* snpfield_search_order, char* a1f
       goto meta_analysis_ret_1;
     }
     if (!extract_ct) {
-      logprint("Error: Empty --extract file.\n");
+      logerrprint("Error: Empty --extract file.\n");
       goto meta_analysis_ret_INVALID_FORMAT;
     }
     if (max_extract_id_len > MAX_ID_LEN_P1) {
-      logprint("Error: --extract IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: --extract IDs are limited to " MAX_ID_LEN_STR " characters.\n");
       goto meta_analysis_ret_INVALID_FORMAT;
     }
     if (wkspace_alloc_c_checked(&sorted_extract_ids, extract_ct * max_extract_id_len)) {
@@ -5640,11 +5685,11 @@ int32_t meta_analysis(char* input_fnames, char* snpfield_search_order, char* a1f
   //    [A1+A2 len] are also included past the end of each entry, to remove the
   //    need for an auxiliary index and let us free the hash table.
   if (!final_variant_ct) {
-    logprint("Error: No --meta-analysis variants.\n");
+    logerrprint("Error: No --meta-analysis variants.\n");
     goto meta_analysis_ret_INVALID_CMDLINE;
 #ifdef __LP64__
   } else if (final_variant_ct > 0x7fffffff) {
-    logprint("Error: Too many distinct --meta-analysis variants (max 2^31 - 1).\n");
+    logerrprint("Error: Too many distinct --meta-analysis variants (max 2^31 - 1).\n");
 #endif
   }
   if (!no_allele) {
@@ -6171,7 +6216,7 @@ int32_t meta_analysis(char* input_fnames, char* snpfield_search_order, char* a1f
     break;
   meta_analysis_ret_INVALID_FORMAT_WW:
     wordwrap(logbuf, 0);
-    logprintb();
+    logerrprintb();
   meta_analysis_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
diff --git a/plink_rserve.c b/plink_rserve.c
index 6aa6144..8c401c1 100644
--- a/plink_rserve.c
+++ b/plink_rserve.c
@@ -112,7 +112,7 @@ int32_t rserve_call(char* rplugin_fname, uint32_t rplugin_port, uint32_t rplugin
     goto rserve_call_ret_READ_FAIL;
   }
   if (inbuf_end == inbuf_start) {
-    logprint("Error: Empty --R file.\n");
+    logerrprint("Error: Empty --R file.\n");
     goto rserve_call_ret_INVALID_FORMAT;
   }
   *inbuf_end = '\0';
@@ -155,7 +155,7 @@ int32_t rserve_call(char* rplugin_fname, uint32_t rplugin_port, uint32_t rplugin
     ii = rc->connect();
     if (ii) {
       sockerrorchecks(tbuf, 128, -1);
-      LOGPRINTFWW("Error: Unable to connect (code %d: %s).\n", ii, tbuf);
+      LOGERRPRINTFWW("Error: Unable to connect (code %d: %s).\n", ii, tbuf);
       goto rserve_call_ret_NETWORK;
     }
     rc->eval("options(echo=F)");
@@ -214,7 +214,10 @@ int32_t rserve_call(char* rplugin_fname, uint32_t rplugin_port, uint32_t rplugin
       fwrite(tbuf, 1, (uintptr_t)(bufptr - tbuf), outfile);
       fputs(" ) \nCOVAR <- matrix( c , nrow = n , byrow=T)\n", outfile);
     } else {
-      fputs("COVAR <- matrix( NA , nrow = n , ncol = 0 , byrow = T)\n", outfile);
+      // old code (this might be better?  but --R backward compatibility is
+      // more important than --R-debug...):
+      // fputs("COVAR <- matrix( NA , nrow = n , ncol = 0 , byrow = T)\n", outfile);
+      fputs("COVAR<-NA\n", outfile);
     }
     fputs("CLUSTER <- c( ", outfile);
     for (sample_idx = 0; sample_idx < pheno_nm_ct - 1; sample_idx++) {
@@ -224,6 +227,7 @@ int32_t rserve_call(char* rplugin_fname, uint32_t rplugin_port, uint32_t rplugin
     }
     bufptr = int32_write(tbuf, sample_to_cluster[sample_idx]);
     bufptr = memcpya(bufptr, " ) \n", 4);
+    fputs("CLUSTER[CLUSTER==-1] <- NA\n", outfile);
     if (fwrite_checked(tbuf, bufptr - tbuf, outfile)) {
       goto rserve_call_ret_WRITE_FAIL;
     }
@@ -421,7 +425,7 @@ int32_t rserve_call(char* rplugin_fname, uint32_t rplugin_port, uint32_t rplugin
     retval = RET_WRITE_FAIL;
     break;
   rserve_call_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   rserve_call_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
diff --git a/plink_set.c b/plink_set.c
index 25fc84b..59e7ba2 100644
--- a/plink_set.c
+++ b/plink_set.c
@@ -350,24 +350,24 @@ int32_t load_range_list(FILE* infile, uint32_t track_set_names, uint32_t border_
       if (fail_on_no_sets) {
 	if (marker_pos) {
 	  // okay, this is a kludge
-	  logprint("Error: All variants excluded by --gene{-all}, since no sets were defined from\n--make-set file.\n");
+	  logerrprint("Error: All variants excluded by --gene{-all}, since no sets were defined from\n--make-set file.\n");
 	  retval = RET_ALL_MARKERS_EXCLUDED;
 	} else {
 	  if (subset_ct) {
-	    logprint("Error: No --gene-subset genes present in --gene-report file.\n");
+	    logerrprint("Error: No --gene-subset genes present in --gene-report file.\n");
 	  } else {
-	    logprint("Error: Empty --gene-report file.\n");
+	    logerrprint("Error: Empty --gene-report file.\n");
 	  }
 	  retval = RET_INVALID_FORMAT;
 	}
 	goto load_range_list_ret_1;
       }
-      LOGPRINTF("Warning: No valid ranges in %s file.\n", file_descrip);
+      LOGERRPRINTF("Warning: No valid ranges in %s file.\n", file_descrip);
       goto load_range_list_ret_1;
     }
     max_set_id_len += c_prefix;
     if (max_set_id_len > MAX_ID_LEN_P1) {
-      logprint("Error: Set IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: Set IDs are limited to " MAX_ID_LEN_STR " characters.\n");
       goto load_range_list_ret_INVALID_FORMAT;
     }
     wkspace_left -= *topsize_ptr;
@@ -530,7 +530,7 @@ int32_t load_range_list(FILE* infile, uint32_t track_set_names, uint32_t border_
     retval = RET_NOMEM;
     break;
   load_range_list_ret_INVALID_FORMAT_2:
-    logprintb();
+    logerrprintb();
   load_range_list_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -581,15 +581,14 @@ int32_t extract_exclude_range(char* fname, uint32_t* marker_pos, uintptr_t unfil
   }
   *marker_exclude_ct_ptr = popcount_longs(marker_exclude, unfiltered_marker_ctl);
   if (*marker_exclude_ct_ptr == unfiltered_marker_ct) {
-    sprintf(logbuf, "Error: All variants excluded by '--%s range'.\n", is_exclude? "exclude" : "extract");
+    LOGERRPRINTF("Error: All variants excluded by '--%s range'.\n", is_exclude? "exclude" : "extract");
     retval = RET_ALL_MARKERS_EXCLUDED;
   } else if (*marker_exclude_ct_ptr == orig_marker_exclude_ct) {
-    sprintf(logbuf, "Warning: No variants excluded by '--%s range'.\n", is_exclude? "exclude" : "extract");
+    LOGERRPRINTF("Warning: No variants excluded by '--%s range'.\n", is_exclude? "exclude" : "extract");
   } else {
     orig_marker_exclude_ct = *marker_exclude_ct_ptr - orig_marker_exclude_ct;
-    sprintf(logbuf, "--%s range: %" PRIuPTR " variant%s excluded.\n", is_exclude? "exclude" : "extract", orig_marker_exclude_ct, (orig_marker_exclude_ct == 1)? "" : "s");
+    LOGPRINTF("--%s range: %" PRIuPTR " variant%s excluded.\n", is_exclude? "exclude" : "extract", orig_marker_exclude_ct, (orig_marker_exclude_ct == 1)? "" : "s");
   }
-  logprintb();
   while (0) {
   extract_exclude_range_ret_NOMEM:
     retval = RET_NOMEM;
@@ -1023,7 +1022,7 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
 	bufptr = &(bufptr[slen]);
       } while (*bufptr);
       if (!genekeep_ct) {
-	logprint("Error: All variants excluded by --gene.\n");
+	logerrprint("Error: All variants excluded by --gene.\n");
 	goto define_sets_ret_ALL_MARKERS_EXCLUDED_2;
       }
       sorted_genekeep_ids = (char*)top_alloc(&topsize, genekeep_ct * max_genekeep_len);
@@ -1052,7 +1051,7 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
       retval = scan_token_ct_len(infile, tbuf, MAXLINELEN, &subset_ct, &max_subset_id_len);
       if (retval) {
 	if (retval == RET_INVALID_FORMAT) {
-	  logprint("Error: Pathologically long token in --subset file.\n");
+	  logerrprint("Error: Pathologically long token in --subset file.\n");
 	}
 	goto define_sets_ret_1;
       }
@@ -1064,9 +1063,9 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
     if (!subset_ct) {
       if ((gene_all || sip->genekeep_flattened) && ((!sip->merged_set_name) || (!complement_sets))) {
 	if (sip->subset_fname) {
-	  logprint("Error: All variants excluded, since --subset file is empty.\n");
+	  logerrprint("Error: All variants excluded, since --subset file is empty.\n");
 	} else {
-	  logprint("Error: All variants excluded, since --set-names was given no parameters.\n");
+	  logerrprint("Error: All variants excluded, since --set-names was given no parameters.\n");
 	}
 	goto define_sets_ret_ALL_MARKERS_EXCLUDED_2;
       }
@@ -1074,15 +1073,15 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
 	goto define_sets_merge_nothing;
       } else {
 	if (sip->subset_fname) {
-          logprint("Warning: Empty --subset file; no sets defined.\n");
+          logerrprint("Warning: Empty --subset file; no sets defined.\n");
 	} else {
-          logprint("Warning: No sets defined since --set-names was given no parameters.\n");
+          logerrprint("Warning: No sets defined since --set-names was given no parameters.\n");
 	}
         goto define_sets_ret_1;
       }
     }
     if (max_subset_id_len > MAX_ID_LEN_P1) {
-      logprint("Error: Subset IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: Subset IDs are limited to " MAX_ID_LEN_STR " characters.\n");
       goto define_sets_ret_INVALID_FORMAT;
     }
     sorted_subset_ids = (char*)top_alloc(&topsize, subset_ct * max_subset_id_len);
@@ -1210,7 +1209,7 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
           curtoklen = (uintptr_t)(bufptr2 - bufptr);
           if (bufptr2 == &(tbuf[MAXLINELEN * 2])) {
 	    if (curtoklen > MAXLINELEN) {
-	      logprint("Error: Excessively long token in --set file.\n");
+	      logerrprint("Error: Excessively long token in --set file.\n");
 	      goto define_sets_ret_INVALID_FORMAT;
 	    }
             bufptr3 = &(tbuf[MAXLINELEN - curtoklen]);
@@ -1252,10 +1251,10 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
       }
       if (!set_ct) {
 	if (!complement_sets) {
-	  logprint("Error: All variants excluded by --gene{-all}, since no sets were defined from\n--set file.\n");
+	  logerrprint("Error: All variants excluded by --gene{-all}, since no sets were defined from\n--set file.\n");
 	  goto define_sets_ret_ALL_MARKERS_EXCLUDED_2;
 	}
-	logprint("Warning: No sets defined from --set file.\n");
+	logerrprint("Warning: No sets defined from --set file.\n");
 	goto define_sets_ret_1;
       }
     }
@@ -1339,7 +1338,7 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
       }
     }
     if (!set_ct) {
-      logprint("Warning: No sets defined from --set file.\n");
+      logerrprint("Warning: No sets defined from --set file.\n");
       goto define_sets_ret_1;
     }
     rewind(infile);
@@ -1356,7 +1355,7 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
       set_ct = 1;
       max_set_id_len = strlen(sip->merged_set_name) + 1;
       if (max_set_id_len > MAX_ID_LEN_P1) {
-	logprint("Error: Set IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+	logerrprint("Error: Set IDs are limited to " MAX_ID_LEN_STR " characters.\n");
 	goto define_sets_ret_INVALID_FORMAT;
       }
       if (wkspace_alloc_c_checked(&set_names, max_set_id_len)) {
@@ -1365,7 +1364,7 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
       memcpy(set_names, sip->merged_set_name, max_set_id_len);
     } else {
       if (max_set_id_len > MAX_ID_LEN_P1) {
-	logprint("Error: Set IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+	logerrprint("Error: Set IDs are limited to " MAX_ID_LEN_STR " characters.\n");
 	goto define_sets_ret_INVALID_FORMAT;
       }
       if (wkspace_alloc_c_checked(&set_names, set_ct * max_set_id_len)) {
@@ -1621,16 +1620,16 @@ int32_t define_sets(Set_info* sip, uintptr_t unfiltered_marker_ct, uintptr_t* ma
     retval = RET_READ_FAIL;
     break;
   define_sets_ret_ALL_MARKERS_EXCLUDED:
-    logprint("Error: All variants excluded by --gene/--gene-all.\n");
+    logerrprint("Error: All variants excluded by --gene/--gene-all.\n");
   define_sets_ret_ALL_MARKERS_EXCLUDED_2:
     retval = RET_ALL_MARKERS_EXCLUDED;
     break;
   define_sets_ret_INVALID_FORMAT_EXTRA_END:
-    logprint("Error: Extra 'END' token in --set file.\n");
+    logerrprint("Error: Extra 'END' token in --set file.\n");
     retval = RET_INVALID_FORMAT;
     break;
   define_sets_ret_INVALID_FORMAT_NO_END:
-    logprint("Error: Last token in --set file isn't 'END'.\n");
+    logerrprint("Error: Last token in --set file isn't 'END'.\n");
   define_sets_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -2308,8 +2307,6 @@ int32_t load_range_list_sortpos(char* fname, uint32_t border_extend, uintptr_t s
 }
 
 int32_t annotate(Annot_info* aip, char* outname, char* outname_end, double pfilter, Chrom_info* chrom_info_ptr) {
-  // logprint("Error: --annotate is currently under development.\n");
-  // return RET_CALC_NOT_YET_SUPPORTED;
   unsigned char* wkspace_mark = wkspace_base;
   gzFile gz_attribfile = NULL;
   FILE* infile = NULL;
@@ -2428,7 +2425,7 @@ int32_t annotate(Annot_info* aip, char* outname, char* outname_end, double pfilt
       retval = scan_token_ct_len(infile, tbuf, MAXLINELEN, &snplist_ct, &max_snplist_id_len);
       if (retval) {
 	if (retval == RET_INVALID_FORMAT) {
-	  logprint("Error: Pathologically long token in --annotate snps file.\n");
+	  logerrprint("Error: Pathologically long token in --annotate snps file.\n");
 	}
 	goto annotate_ret_1;
       }
@@ -2618,16 +2615,16 @@ int32_t annotate(Annot_info* aip, char* outname, char* outname_end, double pfilt
 	retval = scan_token_ct_len(infile, tbuf, MAXLINELEN, &subset_ct, &max_subset_id_len);
 	if (retval) {
 	  if (retval == RET_INVALID_FORMAT) {
-	    logprint("Error: Pathologically long token in --annotate subset file.\n");
+	    logerrprint("Error: Pathologically long token in --annotate subset file.\n");
 	  }
 	  goto annotate_ret_1;
 	}
 	if (!subset_ct) {
-	  logprint("Error: --annotate subset file is empty.\n");
+	  logerrprint("Error: --annotate subset file is empty.\n");
 	  goto annotate_ret_INVALID_FORMAT;
 	}
 	if (max_subset_id_len > MAX_ID_LEN_P1) {
-	  logprint("Error: --annotate subset IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+	  logerrprint("Error: --annotate subset IDs are limited to " MAX_ID_LEN_STR " characters.\n");
 	  goto annotate_ret_INVALID_FORMAT;
 	}
 	sorted_subset_ids = (char*)top_alloc(&topsize, subset_ct * max_subset_id_len);
@@ -2676,7 +2673,7 @@ int32_t annotate(Annot_info* aip, char* outname, char* outname_end, double pfilt
       // need [range_names idx -> merged natural sort order] and
       // [attribute idx -> merged natural sort order] lookup tables
       if (ulii > 0x3fffffff) {
-	logprint("Error: Too many unique annotations for '--annotate block' (max 1073741823).\n");
+	logerrprint("Error: Too many unique annotations for '--annotate block' (max 1073741823).\n");
         goto annotate_ret_INVALID_FORMAT;
       }
       if (wkspace_alloc_ui_checked(&range_idx_lookup, ulii * sizeof(int32_t))) {
@@ -2753,7 +2750,7 @@ int32_t annotate(Annot_info* aip, char* outname, char* outname_end, double pfilt
 #endif
     LOGPRINTF("--annotate block: %u unique annotation%s present.\n", unique_annot_ct, (unique_annot_ct == 1)? "" : "s");
     if (unique_annot_ct > 1000) {
-      logprint("Warning: Output file may be very large.  Are you sure you want >1000 additional\ncolumns per line?  If not, restart without 'block'.\n");
+      logerrprint("Warning: Output file may be very large.  Are you sure you want >1000 additional\ncolumns per line?  If not, restart without 'block'.\n");
     }
     wkspace_left -= topsize;
     if (wkspace_alloc_c_checked(&writebuf, unique_annot_ctlw * sizeof(intptr_t))) {
@@ -3139,7 +3136,7 @@ int32_t annotate(Annot_info* aip, char* outname, char* outname_end, double pfilt
     break;
   annotate_ret_INVALID_FORMAT_WW:
     wordwrap(logbuf, 0);
-    logprintb();
+    logerrprintb();
   annotate_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
@@ -3219,16 +3216,16 @@ int32_t gene_report(char* fname, char* glist, char* subset_fname, uint32_t borde
     retval = scan_token_ct_len(infile, tbuf, MAXLINELEN, &subset_ct, &max_subset_id_len);
     if (retval) {
       if (retval == RET_INVALID_FORMAT) {
-	logprint("Error: Pathologically long token in --gene-subset file.\n");
+	logerrprint("Error: Pathologically long token in --gene-subset file.\n");
       }
       goto gene_report_ret_1;
     }
     if (!subset_ct) {
-      logprint("Error: --gene-subset file is empty.\n");
+      logerrprint("Error: --gene-subset file is empty.\n");
       goto gene_report_ret_INVALID_FORMAT;
     }
     if (max_subset_id_len > MAX_ID_LEN_P1) {
-      logprint("Error: --gene-subset IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: --gene-subset IDs are limited to " MAX_ID_LEN_STR " characters.\n");
       goto gene_report_ret_INVALID_FORMAT;
     }
     sorted_subset_ids = (char*)top_alloc(&topsize, subset_ct * max_subset_id_len);
@@ -3255,11 +3252,11 @@ int32_t gene_report(char* fname, char* glist, char* subset_fname, uint32_t borde
       goto gene_report_ret_1;
     }
     if (!extract_ct) {
-      logprint("Error: Empty --extract file.\n");
+      logerrprint("Error: Empty --extract file.\n");
       goto gene_report_ret_INVALID_FORMAT;
     }
     if (max_extract_id_len > MAX_ID_LEN_P1) {
-      logprint("Error: --extract IDs are limited to " MAX_ID_LEN_STR " characters.\n");
+      logerrprint("Error: --extract IDs are limited to " MAX_ID_LEN_STR " characters.\n");
       goto gene_report_ret_INVALID_FORMAT;
     }
     wkspace_left -= topsize;
@@ -3589,7 +3586,7 @@ int32_t gene_report(char* fname, char* glist, char* subset_fname, uint32_t borde
   while (0) {
   gene_report_ret_LONG_LINE:
     if (loadbuf_size == MAXLINEBUFLEN) {
-      LOGPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, fname);
+      LOGERRPRINTFWW("Error: Line %" PRIuPTR " of %s is pathologically long.\n", line_idx, fname);
       retval = RET_INVALID_FORMAT;
       break;
     }
@@ -3609,7 +3606,7 @@ int32_t gene_report(char* fname, char* glist, char* subset_fname, uint32_t borde
     break;
   gene_report_ret_INVALID_FORMAT_WW:
     wordwrap(logbuf, 0);
-    logprintb();
+    logerrprintb();
   gene_report_ret_INVALID_FORMAT:
     retval = RET_INVALID_FORMAT;
     break;
diff --git a/plink_stats.c b/plink_stats.c
index 9a91b57..60fea0a 100644
--- a/plink_stats.c
+++ b/plink_stats.c
@@ -214,7 +214,7 @@ double SNPHWE2(int32_t obs_hets, int32_t obs_hom1, int32_t obs_hom2, uint32_t mi
     // tail 1 = upper
     while (curr_hets_t2 > 1.5) {
       // het_probs[curr_hets] = 1
-      // het_probs[curr_hets - 2] = het_probs[curr_hets] * curr_hets * (curr_hets
+      // het_probs[curr_hets - 2] = het_probs[curr_hets] * curr_hets * (curr_hets - 1.0)
       curr_homr_t2 += 1;
       curr_homc_t2 += 1;
       lastp2 *= (curr_hets_t2 * (curr_hets_t2 - 1)) / (4 * curr_homr_t2 * curr_homc_t2);
@@ -397,18 +397,11 @@ int32_t SNPHWE_t(int32_t obs_hets, int32_t obs_hom1, int32_t obs_hom2, double th
     }
 
     // An initial upper bound on the tail sum is useful, since it lets us
-    // report test failure before summing the entire center.
-    // The immediate tail is easy: if the next element out on the tail is r,
-    // then 1 + r + r^2 + ... = 1 / (1-r) works.
-    // For the far tail, we currently use the trivial bound of
-    //   1 + floor[het_exp_floor / 2]
-    // (each far tail element must be no greater than 1 and there are at
-    // most that many of them).  This bound could be improved, but it might not
-    // be worth the precomputational effort.
-    // ...and as long as we're using such a weak bound for the far tail,
-    // there's no point to carefully calculating the near tail since we're very
-    // unlikely to use the partial result before exiting from the function.
-    exit_thresh = rare_copies * thresh * EXACT_TEST_BIAS;
+    // report test failure before summing the entire center.  We use the
+    // trivial bound of 1 + floor(rare_copies / 2): that's the total number
+    // of possible het counts, and the relative probability for each count must
+    // be <= 1 if it's in the tail.
+    exit_thresh = (1 + (rare_copies / 2)) * thresh * EXACT_TEST_BIAS;
 
     // het_probs[curr_hets] = 1
     // het_probs[curr_hets - 2] = het_probs[curr_hets] * curr_hets * (curr_hets - 1) / (4 * (curr_homr + 1) * (curr_homc + 1))
@@ -487,7 +480,7 @@ int32_t SNPHWE_t(int32_t obs_hets, int32_t obs_hom1, int32_t obs_hom2, double th
     if (!obs_homr) {
       return 0;
     }
-    exit_thresh = rare_copies * thresh * EXACT_TEST_BIAS;
+    exit_thresh = (1 + (rare_copies / 2)) * thresh * EXACT_TEST_BIAS;
     do {
       curr_hets_t2 += 2;
       lastp2 *= (4 * curr_homr_t2 * curr_homc_t2) / (curr_hets_t2 * (curr_hets_t2 - 1));
@@ -598,7 +591,7 @@ int32_t SNPHWE_midp_t(int32_t obs_hets, int32_t obs_hom1, int32_t obs_hom2, doub
     if (obs_hets < 2) {
       return 0;
     }
-    exit_thresh = rare_copies * thresh * EXACT_TEST_BIAS;
+    exit_thresh = (1 + (rare_copies / 2)) * thresh * EXACT_TEST_BIAS;
     do {
       curr_homr_t2 += 1;
       curr_homc_t2 += 1;
@@ -679,7 +672,7 @@ int32_t SNPHWE_midp_t(int32_t obs_hets, int32_t obs_hom1, int32_t obs_hom2, doub
     if (!obs_homr) {
       return 0;
     }
-    exit_thresh = rare_copies * thresh * EXACT_TEST_BIAS;
+    exit_thresh = (1 + (rare_copies / 2)) * thresh * EXACT_TEST_BIAS;
     do {
       curr_hets_t2 += 2;
       lastp2 *= (4 * curr_homr_t2 * curr_homc_t2) / (curr_hets_t2 * (curr_hets_t2 - 1));

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/plink1.9.git



More information about the debian-med-commit mailing list