Write progress bar to a tty if available

Change-Id: Id9e197898ed598804db1855ae894a76f09aba310
diff --git a/Readme.pod b/Readme.pod
index 4a058c3..28bf016 100644
--- a/Readme.pod
+++ b/Readme.pod
@@ -139,8 +139,11 @@
 
 =item B<--progress|-p>
 
-Show a progress bar (including ETA).
-This option is ignored if valid input is not read from a file.
+Show a progress bar (including ETA) written directly to C</dev/tty>,
+so it always appears on the terminal regardless of C<stderr> redirection.
+This option is ignored if valid input is not read from a file,
+or if no controlling terminal is available (e.g. in a detached container
+or CI environment).
 
 =item B<--output|-o>
 
diff --git a/script/tei2korapxml b/script/tei2korapxml
index 953fc44..2070782 100755
--- a/script/tei2korapxml
+++ b/script/tei2korapxml
@@ -110,13 +110,18 @@
   $what = qr!$what!;
 };
 
+my $progress_fh;
 if ($progress) {
   eval {
     require Time::Progress;
+    my $tty = $^O eq 'MSWin32' ? 'CON' : '/dev/tty';
+    open($progress_fh, '>', $tty)
+      or die "Cannot open $tty";
+    $progress_fh->autoflush(1);
     1;
   } or do {
-     $log->warn('Time::Progress not installed. Progress bar disabled.');
-     $progress = 0;
+    $log->warn('Progress bar disabled: ' . ($@ =~ s/ at .*//sr));
+    $progress = 0;
   }
 };
 
@@ -269,7 +274,7 @@
   while (<$input_fh>) {
 
     if ($p && ($i++ % 500 == 0)) {
-        print STDERR $p->report("\r%20b %p  ETA: %E", tell($input_fh));
+        print $progress_fh $p->report("\r%20b %p  ETA: %E", tell($input_fh));
     };
 
     # remove HTML (multi-line) comments (<!--...-->)
@@ -550,7 +555,7 @@
   $text_id_esc = $auto_textsigle if ($auto_textsigle);
 
   if ($p) {
-      print STDERR $p->report("\r%20b %p  ETA: %E\n", tell($input_fh));
+      print $progress_fh $p->report("\r%20b %p  ETA: %E\n", tell($input_fh));
   };
 } while (($input_fname = shift(@ARGV)) && open($input_fh, '<', $input_fname));
 $zipper->close;