Imported Upstream version 2.6.1
[debian/amanda] / restore-src / restore.c
index 35818efec636aefba7ef59b310191248bf9009ac..828a41441cb17785e3d464937050125490e59dac 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: restore.c,v 1.28 2006/03/14 13:12:01 martinea Exp $
+ * $Id: restore.c 6512 2007-05-24 17:00:24Z ian $
  *
  * retrieves files from an amanda tape
  */
 
 #include "amanda.h"
-#include "tapeio.h"
 #include "util.h"
 #include "restore.h"
 #include "find.h"
 #include "changer.h"
 #include "logfile.h"
 #include "fileheader.h"
+#include "arglist.h"
+#include "cmdline.h"
+#include "server_util.h"
 #include <signal.h>
+#include <timestamp.h>
+
+#include <device.h>
+#include <queueing.h>
+#include <glib.h>
+
+typedef enum {
+    LOAD_NEXT = 1,     /* An unknown new slot has been loaded. */
+    LOAD_CHANGER = -2, /* The requested slot has been loaded. */
+    LOAD_STOP = -1,    /* The search is complete. */
+} LoadStatus;
+
+typedef enum {
+    RESTORE_STATUS_NEXT_FILE,
+    RESTORE_STATUS_NEXT_TAPE,
+    RESTORE_STATUS_STOP
+} RestoreFileStatus;
 
 int file_number;
 
 /* stuff we're stuck having global */
-static long blocksize = -1;
-static char *cur_tapedev = NULL;
-static char *searchlabel = NULL;
 static int backwards;
 static int exitassemble = 0;
-static int tapefd, nslots;
 
 char *rst_conf_logdir = NULL;
 char *rst_conf_logfile = NULL;
-char *curslot = NULL;
+static char *curslot = NULL;
 
 typedef struct open_output_s {
     struct open_output_s *next;
@@ -61,47 +76,80 @@ typedef struct open_output_s {
     int outfd;
 } open_output_t;
 
-
 typedef struct dumplist_s {
     struct dumplist_s *next;
     dumpfile_t *file;
 } dumplist_t;
 
+struct seentapes_s {
+    struct seentapes_s *next;
+    char *slotstr;
+    char *label;
+    dumplist_t *files;
+};
+
 static open_output_t *open_outputs = NULL;
 static dumplist_t *alldumps_list = NULL;
 
-static ssize_t get_block P((int tapefd, char *buffer, int isafile));
-static void append_file_to_fd P((char *filename, int fd));
-static int headers_equal P((dumpfile_t *file1, dumpfile_t *file2, int ignore_partnums));
-static int already_have_dump P((dumpfile_t *file));
-
 /* local functions */
 
-static void handle_sigint(sig)
-int sig;
+static void append_file_to_fd(char *filename, int fd);
+static int headers_equal(dumpfile_t *file1, dumpfile_t *file2, int ignore_partnums);
+static int already_have_dump(dumpfile_t *file);
+static void handle_sigint(int sig);
+static int scan_init(void *ud, int rc, int ns, int bk, int s);
+static Device * conditional_device_open(char * tapedev, FILE * orompt_out,
+                                        rst_flags_t * flags,
+                                        am_feature_t * their_features,
+                                        tapelist_t * desired_tape);
+int loadlabel_slot(void *ud, int rc, char *slotstr, char *device);
+char *label_of_current_slot(char *cur_tapedev, FILE *prompt_out,
+                           int *tapefd, dumpfile_t *file, rst_flags_t *flags,
+                           am_feature_t *their_features,
+                           ssize_t *read_result, tapelist_t *desired_tape);
+
+LoadStatus load_next_tape(char **cur_tapedev, FILE *prompt_out, int backwards,
+                  rst_flags_t *flags, am_feature_t *their_features,
+                  tapelist_t *desired_tape);
+LoadStatus load_manual_tape(char **cur_tapedev, FILE *prompt_out, FILE *prompt_in,
+                    rst_flags_t *flags, am_feature_t *their_features,
+                    tapelist_t *desired_tape);
+
 /*
  * We might want to flush any open dumps and unmerged splits before exiting
  * on SIGINT, so do so.
  */
+static void
+handle_sigint(
+    int                sig)
 {
+    (void)sig; /* Quiet unused parameter warning */
+
     flush_open_outputs(exitassemble, NULL);
-    if(rst_conf_logfile) unlink(rst_conf_logfile);
+    if (rst_conf_logfile) {
+       unlink(rst_conf_logfile);
+       log_add(L_INFO, "pid-done %ld\n", (long)getpid());
+    }
     exit(0);
 }
 
-int lock_logfile()
+int
+lock_logfile(void)
 {
-    rst_conf_logdir = getconf_str(CNF_LOGDIR);
-    if (*rst_conf_logdir == '/') {
-       rst_conf_logdir = stralloc(rst_conf_logdir);
-    } else {
-       rst_conf_logdir = stralloc2(config_dir, rst_conf_logdir);
-    }
+    rst_conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
     rst_conf_logfile = vstralloc(rst_conf_logdir, "/log", NULL);
     if (access(rst_conf_logfile, F_OK) == 0) {
-       error("%s exists: amdump or amflush is already running, or you must run amcleanup", rst_conf_logfile);
+       run_amcleanup(get_config_name());
+    }
+    if (access(rst_conf_logfile, F_OK) == 0) {
+       char *process_name = get_master_process(rst_conf_logfile);
+       dbprintf(_("%s exists: %s is already running, "
+                 "or you must run amcleanup\n"), rst_conf_logfile,
+                process_name);
+       amfree(process_name);
+       return 0;
     }
-    log_add(L_INFO, get_pname());
+    log_add(L_INFO, "%s", get_pname());
     return 1;
 }
 
@@ -110,9 +158,11 @@ int lock_logfile()
  * number, and datestamp, and 0 if not.  The part number can be optionally
  * ignored.
  */
-int headers_equal (file1, file2, ignore_partnums)
-dumpfile_t *file1, *file2;
-int ignore_partnums;
+int
+headers_equal(
+    dumpfile_t *file1,
+    dumpfile_t *file2,
+    int                ignore_partnums)
 {
     if(!file1 || !file2) return(0);
     
@@ -132,8 +182,9 @@ int ignore_partnums;
  * See whether we're already pulled an exact copy of the given file (chunk
  * number and all).  Returns 0 if not, 1 if so.
  */
-int already_have_dump(file)
-dumpfile_t *file;
+int
+already_have_dump(
+    dumpfile_t *file)
 {
     dumplist_t *fileentry = NULL;
 
@@ -148,123 +199,154 @@ dumpfile_t *file;
  * Open the named file and append its contents to the (hopefully open) file
  * descriptor supplies.
  */
-static void append_file_to_fd(filename, fd)
-char *filename;
-int fd;
+static void
+append_file_to_fd(
+    char *     filename,
+    int                write_fd)
 {
-    ssize_t bytes_read;
-    ssize_t s;
-    off_t wc = 0;
-    char *buffer;
-
-    if(blocksize == -1)
-       blocksize = DISK_BLOCK_BYTES;
-    buffer = alloc(blocksize);
+    queue_fd_t queue_fd_write = {write_fd, NULL};
+    queue_fd_t queue_fd_read = {0, NULL};
+    
 
-    if((tapefd = open(filename, O_RDONLY)) == -1) {
-       error("can't open %s: %s", filename, strerror(errno));
-       /* NOTREACHED */
+    queue_fd_read.fd = robust_open(filename, O_RDONLY, 0);
+    if (queue_fd_read.fd < 0) {
+       error(_("can't open %s: %s"), filename, strerror(errno));
+       /*NOTREACHED*/
     }
 
-    for (;;) {
-       bytes_read = get_block(tapefd, buffer, 1); /* same as isafile = 1 */
-       if(bytes_read < 0) {
-           error("read error: %s", strerror(errno));
-           /* NOTREACHED */
-       }
-
-       if (bytes_read == 0)
-               break;
-
-       s = fullwrite(fd, buffer, bytes_read);
-       if (s < bytes_read) {
-           fprintf(stderr,"Error %d (%s) offset " OFF_T_FMT "+" AM64_FMT ", wrote " AM64_FMT "\n",
-                       errno, strerror(errno), wc, (am64_t)bytes_read, (am64_t)s);
-           if (s < 0) {
-               if((errno == EPIPE) || (errno == ECONNRESET)) {
-                   error("%s: pipe reader has quit in middle of file.\n",
-                       get_pname());
-                   /* NOTREACHED */
-               }
-               error("restore: write error = %s", strerror(errno));
-               /* NOTREACHED */
-           }
-           error("Short write: wrote %d bytes expected %d\n", s, bytes_read);
-           /* NOTREACHCED */
+    if (!do_consumer_producer_queue(fd_read_producer, &queue_fd_read,
+                                    fd_write_consumer, &queue_fd_write)) {
+       if (queue_fd_read.errmsg && queue_fd_write.errmsg) {
+           error("Error copying data from file \"%s\" to fd %d: %s: %s.\n",
+                 filename, queue_fd_write.fd, queue_fd_read.errmsg,
+                 queue_fd_write.errmsg);
+       } else if (queue_fd_read.errmsg) {
+           error("Error copying data from file \"%s\" to fd %d: %s.\n",
+                 filename, queue_fd_write.fd, queue_fd_read.errmsg);
+       } else if (queue_fd_write.errmsg) {
+           error("Error copying data from file \"%s\" to fd %d: %s.\n",
+                 filename, queue_fd_write.fd, queue_fd_write.errmsg);
+       } else {
+           error("Error copying data from file \"%s\" to fd %d.\n",
+                 filename, queue_fd_write.fd);
        }
-       wc += bytes_read;
+        g_assert_not_reached();
     }
 
-    amfree(buffer);
-    aclose(tapefd);
+    aclose(queue_fd_read.fd);
 }
 
-/*
- * Tape changer support routines, stolen brazenly from amtape
- */
+/* A user_init function for changer_find(). See changer.h for
+   documentation. */
 static int 
-scan_init(ud, rc, ns, bk, s)
-     void * ud;
-     int rc, ns, bk, s;
-{
-    if(rc)
-        error("could not get changer info: %s", changer_resultstr);
-
-    nslots = ns;
+scan_init(G_GNUC_UNUSED void * ud, int rc, G_GNUC_UNUSED int ns,
+          int bk, G_GNUC_UNUSED int s) {
+    if(rc) {
+        error(_("could not get changer info: %s"), changer_resultstr);
+       /*NOTREACHED*/
+    }
     backwards = bk;
 
     return 0;
 }
-int loadlabel_slot(ud, rc, slotstr, device)
-     void *ud;
-int rc;
-char *slotstr;
-char *device;
+
+typedef struct {
+    char ** cur_tapedev;
+    char * searchlabel;
+    rst_flags_t *flags;
+} loadlabel_data;
+
+/* DANGER WILL ROBINSON: This function references globals:
+          char * curslot;
+ */
+int
+loadlabel_slot(void *  datap,
+               int     rc,
+               char *  slotstr,
+               char *  device_name)
 {
-    char *errstr;
-    char *datestamp = NULL;
-    char *label = NULL;
+    loadlabel_data * data = (loadlabel_data*)datap;
+    Device * device;
+    DeviceStatusFlags device_status;
+
+    g_return_val_if_fail(rc > 1 || device_name != NULL, 0);
+    g_return_val_if_fail(slotstr != NULL, 0);
 
+    amfree(curslot);
+
+    if(rc > 1) {
+        error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
+        g_assert_not_reached();
+    }
 
-    if(rc > 1)
-        error("could not load slot %s: %s", slotstr, changer_resultstr);
-    else if(rc == 1)
-        fprintf(stderr, "%s: slot %s: %s\n",
+    if(rc == 1) {
+        g_fprintf(stderr, _("%s: slot %s: %s\n"),
                 get_pname(), slotstr, changer_resultstr);
-    else if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL)
-        fprintf(stderr, "%s: slot %s: %s\n", get_pname(), slotstr, errstr);
-    else {
-        fprintf(stderr, "%s: slot %s: date %-8s label %s",
-                get_pname(), slotstr, datestamp, label);
-        if(strcmp(label, FAKE_LABEL) != 0
-           && strcmp(label, searchlabel) != 0)
-            fprintf(stderr, " (wrong tape)\n");
-        else {
-            fprintf(stderr, " (exact label match)\n");
-            if((errstr = tape_rewind(device)) != NULL) {
-                fprintf(stderr,
-                        "%s: could not rewind %s: %s",
-                        get_pname(), device, errstr);
-                amfree(errstr);
-            }
-           amfree(cur_tapedev);
-           curslot = stralloc(slotstr);
-            amfree(datestamp);
-            amfree(label);
-           if(device)
-               cur_tapedev = stralloc(device);
-            return 1;
-        }
+        return 0;
+    } 
+    
+    device = device_open(device_name);
+    g_assert(device != NULL);
+    if (device->status != DEVICE_STATUS_SUCCESS) {
+        g_fprintf(stderr, "%s: slot %s: Could not open device: %s.\n",
+                get_pname(), slotstr, device_error(device));
+        return 0;
     }
-    amfree(datestamp);
-    amfree(label);
 
-    if(cur_tapedev) amfree(cur_tapedev);
-    curslot = stralloc(slotstr);
-    if(!device) return(1);
-    cur_tapedev = stralloc(device);
+    if (!device_configure(device, TRUE)) {
+        g_fprintf(stderr, "%s: slot %s: Error configuring device:\n"
+                "%s: slot %s: %s\n",
+                get_pname(), slotstr, get_pname(), slotstr, device_error_or_status(device));
+        g_object_unref(device);
+        return 0;
+    }
 
-    return 0;
+    if (!set_restore_device_read_buffer_size(device, data->flags)) {
+        g_fprintf(stderr, "%s: slot %s: Error setting read block size:\n"
+                "%s: slot %s: %s\n",
+                get_pname(), slotstr, get_pname(), slotstr, device_error_or_status(device));
+        g_object_unref(device);
+        return 0;
+    }
+    device_status = device_read_label(device);
+    if (device_status != DEVICE_STATUS_SUCCESS) {
+        g_fprintf(stderr, "%s: slot %s: Error reading tape label:\n"
+                "%s: slot %s: %s\n",
+                get_pname(), slotstr, get_pname(), slotstr, device_error_or_status(device));
+        g_object_unref(device);
+        return 0;
+    }
+
+    g_assert(device->volume_label != NULL);
+    if (device->volume_label == NULL) {
+        g_fprintf(stderr, "%s: slot %s: Could not read tape label.\n",
+                get_pname(), slotstr);
+        g_object_unref(device);
+        return 0;
+    }
+
+    if (!device_start(device, ACCESS_READ, NULL, NULL)) {
+        g_fprintf(stderr, "%s: slot %s: Could not open device for reading: %s.\n",
+                get_pname(), slotstr, device_error(device));
+        return 0;
+    }
+
+    g_fprintf(stderr, "%s: slot %s: time %-14s label %s",
+            get_pname(), slotstr, device->volume_time, device->volume_label);
+
+    if(strcmp(device->volume_label, data->searchlabel) != 0) {
+        g_fprintf(stderr, " (wrong tape)\n");
+        g_object_unref(device);
+        return 0;
+    }
+
+    g_fprintf(stderr, " (exact label match)\n");
+
+    g_object_unref(device);
+    curslot = newstralloc(curslot, slotstr);
+    amfree(*(data->cur_tapedev));
+    *(data->cur_tapedev) = stralloc(device_name);
+    return 1;
 }
 
 
@@ -276,9 +358,10 @@ char *device;
  * Check whether we've read all of the preceding parts of a given split dump,
  * generally used to see if we're done and can close the thing.
  */
-int have_all_parts (file, upto)
-dumpfile_t *file;
-int upto;
+int
+have_all_parts (
+    dumpfile_t *file,
+    int                upto)
 {
     int c;
     int *foundparts = NULL;
@@ -288,7 +371,7 @@ int upto;
 
     if(upto < 1) upto = file->totalparts;
 
-    foundparts = alloc(sizeof(int) * upto); 
+    foundparts = alloc(SIZEOF(*foundparts) * upto); 
     for(c = 0 ; c< upto; c++) foundparts[c] = 0;
     
     for(fileentry=alldumps_list;fileentry; fileentry=fileentry->next){
@@ -321,16 +404,17 @@ int upto;
  * string them together.  If given an optional file header argument, flush
  * only that dump and do not flush/free any others.
  */
-void flush_open_outputs(reassemble, only_file)
-int reassemble;
-dumpfile_t *only_file;
+void
+flush_open_outputs(
+    int                reassemble,
+    dumpfile_t *only_file)
 {
     open_output_t *cur_out = NULL, *prev = NULL;
     find_result_t *sorted_files = NULL;
     amwait_t compress_status;
 
     if(!only_file){
-       fprintf(stderr, "\n");
+       g_fprintf(stderr, "\n");
     }
 
     /*
@@ -353,16 +437,16 @@ dumpfile_t *only_file;
            if(only_file && !headers_equal(cur_file, only_file, 1)){
                continue;
            }
-           cur_find_res = alloc(sizeof(find_result_t));
-           memset(cur_find_res, '\0', sizeof(find_result_t));
-           cur_find_res->datestamp = atoi(cur_file->datestamp);
+           cur_find_res = alloc(SIZEOF(find_result_t));
+           memset(cur_find_res, '\0', SIZEOF(find_result_t));
+           cur_find_res->timestamp = stralloc(cur_file->datestamp);
            cur_find_res->hostname = stralloc(cur_file->name);
            cur_find_res->diskname = stralloc(cur_file->disk);
            cur_find_res->level = cur_file->dumplevel;
            if(cur_file->partnum < 1) cur_find_res->partnum = stralloc("--");
            else{
                char part_str[NUM_STR_SIZE];
-               snprintf(part_str, sizeof(part_str), "%d", cur_file->partnum);
+               g_snprintf(part_str, SIZEOF(part_str), "%d", cur_file->partnum);
                cur_find_res->partnum = stralloc(part_str);
            }
            cur_find_res->user_ptr = (void*)cur_out;
@@ -390,31 +474,41 @@ dumpfile_t *only_file;
                /* is it a continuation of one we've been writing? */
                if(main_file && cur_file->partnum > lastpartnum &&
                        headers_equal(cur_file, main_file, 1)){
+                   char *cur_filename;
+                   char *main_filename;
 
                    /* effectively changing filehandles */
                    aclose(cur_out->outfd);
                    cur_out->outfd = outfd;
 
-                   fprintf(stderr, "Merging %s with %s\n",
-                           make_filename(cur_file), make_filename(main_file));
-                   append_file_to_fd(make_filename(cur_file), outfd);
-                   if(unlink(make_filename(cur_file)) < 0){
-                       fprintf(stderr, "Failed to unlink %s: %s\n",
-                                    make_filename(cur_file), strerror(errno));
+                   cur_filename  = make_filename(cur_file);
+                   main_filename = make_filename(main_file);
+                   g_fprintf(stderr, _("Merging %s with %s\n"),
+                           cur_filename, main_filename);
+                   append_file_to_fd(cur_filename, outfd);
+                   if(unlink(cur_filename) < 0){
+                       g_fprintf(stderr, _("Failed to unlink %s: %s\n"),
+                                    cur_filename, strerror(errno));
                    }
+                   amfree(cur_filename);
+                   amfree(main_filename);
                }
                /* or a new file? */
-               else{
+               else {
                    if(outfd >= 0) aclose(outfd);
-                   if(main_file) amfree(main_file);
-                   main_file = alloc(sizeof(dumpfile_t));
-                   memcpy(main_file, cur_file, sizeof(dumpfile_t));
+                   amfree(main_file);
+                   main_file = alloc(SIZEOF(dumpfile_t));
+                   memcpy(main_file, cur_file, SIZEOF(dumpfile_t));
                    outfd = cur_out->outfd;
-                   if(outfd < 0){
-                       if((outfd = open(make_filename(cur_file), O_RDWR|O_APPEND)) < 0){
-                         error("Couldn't open %s for appending: %s\n",
-                               make_filename(cur_file), strerror(errno));
+                   if(outfd < 0) {
+                       char *cur_filename = make_filename(cur_file);
+                       open(cur_filename, O_RDWR|O_APPEND);
+                       if (outfd < 0) {
+                         error(_("Couldn't open %s for appending: %s"),
+                               cur_filename, strerror(errno));
+                         /*NOTREACHED*/
                        }
+                       amfree(cur_filename);
                    }
                }
                lastpartnum = cur_file->partnum;
@@ -437,7 +531,7 @@ dumpfile_t *only_file;
      */
     for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
        dumpfile_t *cur_file = NULL;
-       if(prev) amfree(prev);
+       amfree(prev);
        cur_file = cur_out->file;
        /* if we requested a particular file, do only that one */
        if(only_file && !headers_equal(cur_file, only_file, 1)){
@@ -460,8 +554,9 @@ dumpfile_t *only_file;
 /*
  * Turn a fileheader into a string suited for use on the filesystem.
  */
-char *make_filename(file)
-dumpfile_t *file;
+char *
+make_filename(
+    dumpfile_t *file)
 {
     char number[NUM_STR_SIZE];
     char part[NUM_STR_SIZE];
@@ -469,23 +564,23 @@ dumpfile_t *file;
     char *sfn = NULL;
     char *fn = NULL;
     char *pad = NULL;
-    int padlen = 0;
+    size_t padlen = 0;
 
-    snprintf(number, sizeof(number), "%d", file->dumplevel);
-    snprintf(part, sizeof(part), "%d", file->partnum);
+    g_snprintf(number, SIZEOF(number), "%d", file->dumplevel);
+    g_snprintf(part, SIZEOF(part), "%d", file->partnum);
 
-    if(file->totalparts < 0){
-       snprintf(totalparts, sizeof(totalparts), "UNKNOWN");
+    if(file->totalparts < 0) {
+       g_snprintf(totalparts, SIZEOF(totalparts), "UNKNOWN");
     }
-    else{
-       snprintf(totalparts, sizeof(totalparts), "%d", file->totalparts);
+    else {
+       g_snprintf(totalparts, SIZEOF(totalparts), "%d", file->totalparts);
     }
     padlen = strlen(totalparts) + 1 - strlen(part);
     pad = alloc(padlen);
     memset(pad, '0', padlen);
     pad[padlen - 1] = '\0';
 
-    snprintf(part, sizeof(part), "%s%d", pad, file->partnum);
+    g_snprintf(part, SIZEOF(part), "%s%d", pad, file->partnum);
 
     sfn = sanitise_filename(file->disk);
     fn = vstralloc(file->name,
@@ -496,122 +591,96 @@ dumpfile_t *file;
                   ".",
                   number,
                   NULL);
-    if(file->partnum > 0){
-       fn = vstralloc(fn, ".", part, NULL);
+    if (file->partnum > 0) {
+       vstrextend(&fn, ".", part, NULL);
     }
     amfree(sfn);
     amfree(pad);
     return fn;
 }
 
-
-/*
-XXX Making this thing a lib functiong broke a lot of assumptions everywhere,
-but I think I've found them all.  Maybe.  Damn globals all over the place.
-*/
-static ssize_t get_block(tapefd, buffer, isafile)
-int tapefd, isafile;
-char *buffer;
-{
-    if(isafile)
-       return (fullread(tapefd, buffer, blocksize));
-
-    return(tapefd_read(tapefd, buffer, blocksize));
-}
-
-int disk_match(file, datestamp, hostname, diskname, level)
-dumpfile_t *file;
-char *datestamp, *hostname, *diskname, *level;
-/*
- * Returns 1 if the current dump file matches the hostname and diskname
+/* Returns 1 if the dump file matches the hostname and diskname
  * regular expressions given on the command line, 0 otherwise.  As a 
- * special case, empty regexs are considered equivalent to ".*": they 
- * match everything.
+ * special case, empty regexs and NULLs are considered equivalent to 
+ * ".*": they match everything.
+ *
+ * @param file: the file to examine
+ * @param datestamp: the datestamp regex, or NULL for any
+ * @param hostname: the hostname regex, or NULL for any
+ * @param diskname: the diskname regex, or NULL for any
+ * @param level: the level regex, or NULL for any
+ * @returns: 1 if the dump file matches
  */
+static int
+disk_match(
+    dumpfile_t *file,
+    char *     datestamp,
+    char *     hostname,
+    char *     diskname,
+    char *     level)
 {
     char level_str[NUM_STR_SIZE];
-    snprintf(level_str, sizeof(level_str), "%d", file->dumplevel);
+    g_snprintf(level_str, SIZEOF(level_str), "%d", file->dumplevel);
 
     if(file->type != F_DUMPFILE && file->type != F_SPLIT_DUMPFILE) return 0;
 
-    if((*hostname == '\0' || match_host(hostname, file->name)) &&
-       (*diskname == '\0' || match_disk(diskname, file->disk)) &&
-       (*datestamp == '\0' || match_datestamp(datestamp, file->datestamp)) &&
-       (*level == '\0' || match_level(level, level_str)))
+    if((!hostname || *hostname == '\0' || match_host(hostname, file->name)) &&
+       (!diskname || *diskname == '\0' || match_disk(diskname, file->disk)) &&
+       (!datestamp || *datestamp == '\0' || match_datestamp(datestamp, file->datestamp)) &&
+       (!level || *level == '\0' || match_level(level, level_str)))
        return 1;
     else
        return 0;
 }
 
-
-void read_file_header(file, tapefd, isafile, flags)
-dumpfile_t *file;
-int tapefd;
-int isafile;
-rst_flags_t *flags;
 /*
- * Reads the first block of a tape file.
+ * Reads the first block of a holding disk file.
  */
+
+static gboolean
+read_holding_disk_header(
+    dumpfile_t *       file,
+    int                        tapefd,
+    rst_flags_t *      flags)
 {
-    ssize_t bytes_read;
+    size_t bytes_read;
     char *buffer;
-  
+    size_t blocksize;
+
     if(flags->blocksize > 0)
-       blocksize = flags->blocksize;
-    else if(blocksize == -1)
-       blocksize = DISK_BLOCK_BYTES;
+        blocksize = (size_t)flags->blocksize;
+    else
+        blocksize = DISK_BLOCK_BYTES;
     buffer = alloc(blocksize);
 
-    bytes_read = get_block(tapefd, buffer, isafile);
-    if(bytes_read < 0) {
-       error("error reading file header: %s", strerror(errno));
-       /* NOTREACHED */
-    }
-
+    bytes_read = full_read(tapefd, buffer, blocksize);
     if(bytes_read < blocksize) {
-       if(bytes_read == 0) {
-           fprintf(stderr, "%s: missing file header block\n", get_pname());
+       const char *errtxt;
+       if(errno == 0)
+           errtxt = "Unexpected EOF";
+       else
+           errtxt = strerror(errno);
+
+       if (bytes_read == 0) {
+           g_fprintf(stderr, _("%s: missing file header block: %s\n"), 
+               get_pname(), errtxt);
        } else {
-           fprintf(stderr, "%s: short file header block: " AM64_FMT " byte%s\n",
-                   get_pname(), (am64_t)bytes_read, (bytes_read == 1) ? "" : "s");
+           g_fprintf(stderr,
+                   plural(_("%s: short file header block: %zd byte: %s"),
+                          _("%s: short file header block: %zd bytes: %s\n"),
+                          bytes_read),
+                   get_pname(), bytes_read, errtxt);
        }
        file->type = F_UNKNOWN;
     } else {
-       parse_file_header(buffer, file, bytes_read);
+        parse_file_header(buffer, file, bytes_read);
     }
     amfree(buffer);
+    return (file->type != F_UNKNOWN &&
+            file->type != F_EMPTY &&
+            file->type != F_WEIRD);
 }
 
-
-void drain_file(tapefd, flags)
-int tapefd;
-rst_flags_t *flags;
-{
-    ssize_t bytes_read;
-    char *buffer;
-
-    if(flags->blocksize)
-       blocksize = flags->blocksize;
-    else if(blocksize == -1)
-       blocksize = DISK_BLOCK_BYTES;
-    buffer = alloc(blocksize);
-
-    do {
-       bytes_read = get_block(tapefd, buffer, 0);
-       if(bytes_read < 0) {
-           error("drain read error: %s", strerror(errno));
-       }
-    } while (bytes_read > 0);
-
-    amfree(buffer);
-}
-
-ssize_t restore(file, filename, tapefd, isafile, flags)
-dumpfile_t *file;
-char *filename;
-int tapefd;
-int isafile;
-rst_flags_t *flags;
 /*
  * Restore the current file from tape.  Depending on the settings of
  * the command line flags, the file might need to be compressed or
@@ -620,86 +689,93 @@ rst_flags_t *flags;
  * but with the -p flag the output goes to stdout (and presumably is
  * piped to restore).
  */
+
+
+/* FIXME: Mondo function that needs refactoring. */
+void restore(RestoreSource * source,
+             rst_flags_t *     flags)
 {
     int dest = -1, out;
-    ssize_t s;
     int file_is_compressed;
     int is_continuation = 0;
     int check_for_aborted = 0;
     char *tmp_filename = NULL, *final_filename = NULL;
     struct stat statinfo;
-    open_output_t *myout = NULL, *oldout = NULL;
+    open_output_t *free_myout = NULL, *myout = NULL, *oldout = NULL;
     dumplist_t *tempdump = NULL, *fileentry = NULL;
     char *buffer;
     int need_compress=0, need_uncompress=0, need_decrypt=0;
     int stage=0;
-    ssize_t bytes_read;
     struct pipeline {
         int    pipe[2];
     } pipes[3];
+    char * filename;
+
+    filename = make_filename(source->header);
 
-    if(flags->blocksize)
-       blocksize = flags->blocksize;
-    else if(blocksize == -1)
-       blocksize = DISK_BLOCK_BYTES;
+    memset(pipes, -1, SIZEOF(pipes));
 
-    if(already_have_dump(file)){
-       fprintf(stderr, " *** Duplicate file %s, one is probably an aborted write\n", make_filename(file));
+    if(already_have_dump(source->header)){
+       g_fprintf(stderr, _(" *** Duplicate file %s, one is probably an aborted write\n"), filename);
        check_for_aborted = 1;
     }
 
     /* store a shorthand record of this dump */
-    tempdump = alloc(sizeof(dumplist_t));
-    tempdump->file = alloc(sizeof(dumpfile_t));
+    tempdump = malloc(SIZEOF(dumplist_t));
+    tempdump->file = malloc(SIZEOF(dumpfile_t));
     tempdump->next = NULL;
-    memcpy(tempdump->file, file, sizeof(dumpfile_t));
+    memcpy(tempdump->file, source->header, SIZEOF(dumpfile_t));
 
     /*
      * If we're appending chunked files to one another, and if this is a
      * continuation of a file we just restored, and we've still got the
      * output handle from that previous restore, we're golden.  Phew.
      */
-    if(flags->inline_assemble && file->type == F_SPLIT_DUMPFILE){
+    if(flags->inline_assemble && source->header->type == F_SPLIT_DUMPFILE){
        myout = open_outputs;
        while(myout != NULL){
            if(myout->file->type == F_SPLIT_DUMPFILE &&
-                   headers_equal(file, myout->file, 1)){
-               if(file->partnum == myout->lastpartnum + 1){
+               headers_equal(source->header, myout->file, 1)){
+               if(source->header->partnum == myout->lastpartnum + 1){
                    is_continuation = 1;
                    break;
                }
            }
            myout = myout->next;
        }
-       if(myout != NULL) myout->lastpartnum = file->partnum;
-       else if(file->partnum != 1){
-           fprintf(stderr, "%s:      Chunk out of order, will save to disk and append to output.\n", get_pname());
+       if(myout != NULL) myout->lastpartnum = source->header->partnum;
+       else if(source->header->partnum != 1){
+           g_fprintf(stderr, _("%s:      Chunk out of order, will save to disk and append to output.\n"), get_pname());
            flags->pipe_to_fd = -1;
            flags->compress = 0;
            flags->leave_comp = 1;
        }
        if(myout == NULL){
-           myout = alloc(sizeof(open_output_t));
-           memset(myout, 0, sizeof(open_output_t));
+           free_myout = myout = alloc(SIZEOF(open_output_t));
+           memset(myout, 0, SIZEOF(open_output_t));
        }
     }
     else{
-      myout = alloc(sizeof(open_output_t));
-      memset(myout, 0, sizeof(open_output_t));
+      free_myout = myout = alloc(SIZEOF(open_output_t));
+      memset(myout, 0, SIZEOF(open_output_t));
     }
 
 
     if(is_continuation && flags->pipe_to_fd == -1){
-       fprintf(stderr, "%s:      appending to %s\n", get_pname(),
-                   make_filename(myout->file));
+       char *filename;
+       filename = make_filename(myout->file);
+       g_fprintf(stderr, _("%s:      appending to %s\n"), get_pname(),
+               filename);
+       amfree(filename);
     }
 
     /* adjust compression flag */
-    file_is_compressed = file->compressed;
-    if(!flags->compress && file_is_compressed && !known_compress_type(file)) {
-       fprintf(stderr, 
-               "%s: unknown compression suffix %s, can't uncompress\n",
-               get_pname(), file->comp_suffix);
+    file_is_compressed = source->header->compressed;
+    if(!flags->compress && file_is_compressed &&
+       !known_compress_type(source->header)) {
+       g_fprintf(stderr, 
+               _("%s: unknown compression suffix %s, can't uncompress\n"),
+               get_pname(), source->header->comp_suffix);
        flags->compress = 1;
     }
 
@@ -709,12 +785,12 @@ rst_flags_t *flags;
       out = myout->outfd;
     } else {
       if(flags->pipe_to_fd != -1) {
-         dest = flags->pipe_to_fd;     /* standard output */
+         dest = flags->pipe_to_fd;
       } else {
          char *filename_ext = NULL;
   
          if(flags->compress) {
-             filename_ext = file_is_compressed ? file->comp_suffix
+             filename_ext = file_is_compressed ? source->header->comp_suffix
                                              : COMPRESS_SUFFIX;
          } else if(flags->raw) {
              filename_ext = ".RAW";
@@ -729,12 +805,13 @@ rst_flags_t *flags;
              amfree(tmp_filename);
              tmp_filename = tmpstr;
          } 
-         final_filename = stralloc(tmp_filename); 
-         tmp_filename = newvstralloc(tmp_filename, ".tmp", NULL);
-         if((dest = creat(tmp_filename, CREAT_MODE)) < 0) {
-             error("could not create output file %s: %s",
-                                              tmp_filename, strerror(errno));
-             /*NOTREACHED*/
+         final_filename = tmp_filename; 
+         tmp_filename = vstralloc(final_filename, ".tmp", NULL);
+         if((dest = open(tmp_filename, (O_CREAT | O_RDWR | O_TRUNC),
+                         CREAT_MODE)) < 0) {
+             error(_("could not create output file %s: %s"),
+                   tmp_filename, strerror(errno));
+              /*NOTREACHED*/
          }
          amfree(filename_ext);
       }
@@ -750,13 +827,14 @@ rst_flags_t *flags;
      * it has a fixed size.
      */
     if(flags->raw || (flags->headers && !is_continuation)) {
-       int w;
-       char *cont_filename;
-       dumpfile_t tmp_hdr;
+       ssize_t     w;
+       dumpfile_t  tmp_hdr;
+       char       *dle_str;
 
        if(flags->compress && !file_is_compressed) {
-           file->compressed = 1;
-           snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
+           source->header->compressed = 1;
+           g_snprintf(source->header->uncompress_cmd,
+                     SIZEOF(source->header->uncompress_cmd),
                        " %s %s |", UNCOMPRESS_PATH,
 #ifdef UNCOMPRESS_OPT
                        UNCOMPRESS_OPT
@@ -764,71 +842,79 @@ rst_flags_t *flags;
                        ""
 #endif
                        );
-           strncpy(file->comp_suffix,
+           strncpy(source->header->comp_suffix,
                    COMPRESS_SUFFIX,
-                   sizeof(file->comp_suffix)-1);
-           file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
+                   SIZEOF(source->header->comp_suffix)-1);
+           source->header->comp_suffix[SIZEOF(source->header->comp_suffix)-1]
+                = '\0';
        }
 
-       memcpy(&tmp_hdr, file, sizeof(dumpfile_t));
+       memcpy(&tmp_hdr, source->header, SIZEOF(dumpfile_t));
 
        /* remove CONT_FILENAME from header */
-       cont_filename = stralloc(file->cont_filename);
-       memset(file->cont_filename,'\0',sizeof(file->cont_filename));
-       file->blocksize = DISK_BLOCK_BYTES;
+       memset(source->header->cont_filename, '\0',
+               SIZEOF(source->header->cont_filename));
+       dle_str = clean_dle_str_for_client(source->header->dle_str);
+       source->header->dle_str = dle_str;
+       source->header->blocksize = DISK_BLOCK_BYTES;
 
        /*
         * Dumb down split file headers as well, so that older versions of
         * things like amrecover won't gag on them.
         */
-       if(file->type == F_SPLIT_DUMPFILE && flags->mask_splits){
-           file->type = F_DUMPFILE;
+       if(source->header->type == F_SPLIT_DUMPFILE && flags->mask_splits){
+           source->header->type = F_DUMPFILE;
        }
 
        buffer = alloc(DISK_BLOCK_BYTES);
-       build_header(buffer, file, DISK_BLOCK_BYTES);
+       buffer = build_header(source->header, DISK_BLOCK_BYTES);
 
-       if((w = fullwrite(out, buffer, DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
-           if(w < 0) {
-               error("write error: %s", strerror(errno));
+       if((w = full_write(out, buffer,
+                          DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
+           if(errno != 0) {
+               error(_("write error: %s"), strerror(errno));
+               /*NOTREACHED*/
            } else {
-               error("write error: %d instead of %d", w, DISK_BLOCK_BYTES);
+               error(_("write error: %zd instead of %d"), w, DISK_BLOCK_BYTES);
+               /*NOTREACHED*/
            }
        }
        amfree(buffer);
-       /* add CONT_FILENAME to header */
-#if 0
-//     strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename));
-#endif
-       amfree(cont_filename);
-       memcpy(file, &tmp_hdr, sizeof(dumpfile_t));
+       memcpy(source->header, &tmp_hdr, SIZEOF(dumpfile_t));
     }
  
     /* find out if compression or uncompression is needed here */
     if(flags->compress && !file_is_compressed && !is_continuation
          && !flags->leave_comp
-         && (flags->inline_assemble || file->type != F_SPLIT_DUMPFILE))
+         && (flags->inline_assemble ||
+              source->header->type != F_SPLIT_DUMPFILE))
        need_compress=1;
        
     if(!flags->raw && !flags->compress && file_is_compressed
          && !is_continuation && !flags->leave_comp && (flags->inline_assemble
-         || file->type != F_SPLIT_DUMPFILE))
+         || source->header->type != F_SPLIT_DUMPFILE))
        need_uncompress=1;   
 
-    if(!flags->raw && file->encrypted)
+    if(!flags->raw && source->header->encrypted && !is_continuation &&
+       (flags->inline_assemble || source->header->type != F_SPLIT_DUMPFILE)) {
        need_decrypt=1;
+    }
    
     /* Setup pipes for decryption / compression / uncompression  */
     stage = 0;
     if (need_decrypt) {
-      if (pipe(&pipes[stage].pipe[0]) < 0) 
-        error("error [pipe[%d]: %s]", stage, strerror(errno));
+      if (pipe(&pipes[stage].pipe[0]) < 0) {
+        error(_("error [pipe[%d]: %s]"), stage, strerror(errno));
+       /*NOTREACHED*/
+      }
       stage++;
     }
 
     if (need_compress || need_uncompress) {
-      if (pipe(&pipes[stage].pipe[0]) < 0) 
-        error("error [pipe[%d]: %s]", stage, strerror(errno));
+      if (pipe(&pipes[stage].pipe[0]) < 0) {
+        error(_("error [pipe[%d]: %s]"), stage, strerror(errno));
+       /*NOTREACHED*/
+      }
       stage++;
     }
     pipes[stage].pipe[0] = -1; 
@@ -840,30 +926,43 @@ rst_flags_t *flags;
     if(need_decrypt) {
       switch(myout->comp_enc_pid = fork()) {
       case -1:
-       error("could not fork for decrypt: %s", strerror(errno));
+       error(_("could not fork for decrypt: %s"), strerror(errno));
+       /*NOTREACHED*/
+
       default:
        aclose(pipes[stage].pipe[0]);
        aclose(pipes[stage+1].pipe[1]);
         stage++;
        break;
+
       case 0:
-       if(dup2(pipes[stage].pipe[0], 0) == -1)
-           error("error decrypt stdin [dup2 %d %d: %s]", stage,
+       if(dup2(pipes[stage].pipe[0], 0) == -1) {
+           error(_("error decrypt stdin [dup2 %d %d: %s]"), stage,
                pipes[stage].pipe[0], strerror(errno));
+               /*NOTREACHED*/
+       }
 
-       if(dup2(pipes[stage+1].pipe[1], 1) == -1)
-           error("error decrypt stdout [dup2 %d %d: %s]", stage + 1,
+       if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
+           error(_("error decrypt stdout [dup2 %d %d: %s]"), stage + 1,
                pipes[stage+1].pipe[1], strerror(errno));
+               /*NOTREACHED*/
+       }
 
        safe_fd(-1, 0);
-       if (*file->srv_encrypt) {
-         (void) execlp(file->srv_encrypt, file->srv_encrypt,
-                       file->srv_decrypt_opt, NULL);
-         error("could not exec %s: %s", file->srv_encrypt, strerror(errno));
-       }  else if (*file->clnt_encrypt) {
-         (void) execlp(file->clnt_encrypt, file->clnt_encrypt,
-                       file->clnt_decrypt_opt, NULL);
-         error("could not exec %s: %s", file->clnt_encrypt, strerror(errno));
+       if (source->header->srv_encrypt[0] != '\0') {
+         (void) execlp(source->header->srv_encrypt,
+                        source->header->srv_encrypt,
+                       source->header->srv_decrypt_opt, NULL);
+         error("could not exec %s: %s",
+                source->header->srv_encrypt, strerror(errno));
+          g_assert_not_reached();
+       }  else if (source->header->clnt_encrypt[0] != '\0') {
+         (void) execlp(source->header->clnt_encrypt,
+                        source->header->clnt_encrypt,
+                       source->header->clnt_decrypt_opt, NULL);
+         error("could not exec %s: %s",
+                source->header->clnt_encrypt, strerror(errno));
+          g_assert_not_reached();
        }
       }
     }
@@ -873,29 +972,36 @@ rst_flags_t *flags;
          * Insert a compress pipe
          */
        switch(myout->comp_enc_pid = fork()) {
-       case -1: error("could not fork for %s: %s",
-                      COMPRESS_PATH, strerror(errno));
+       case -1:
+           error(_("could not fork for %s: %s"), COMPRESS_PATH, strerror(errno));
+           /*NOTREACHED*/
+
        default:
            aclose(pipes[stage].pipe[0]);
            aclose(pipes[stage+1].pipe[1]);
             stage++;
            break;
+
        case 0:
-           if(dup2(pipes[stage].pipe[0], 0) == -1)
-               error("error compress stdin [dup2 %d %d: %s]", stage,
+           if(dup2(pipes[stage].pipe[0], 0) == -1) {
+               error(_("error compress stdin [dup2 %d %d: %s]"), stage,
                  pipes[stage].pipe[0], strerror(errno));
+               /*NOTREACHED*/
+           }
 
-           if(dup2(pipes[stage+1].pipe[1], 1) == -1)
-               error("error compress stdout [dup2 %d %d: %s]", stage + 1,
+           if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
+               error(_("error compress stdout [dup2 %d %d: %s]"), stage + 1,
                  pipes[stage+1].pipe[1], strerror(errno));
-
+                 /*NOTREACHED*/
+           }
            if (*flags->comp_type == '\0') {
                flags->comp_type = NULL;
            }
 
            safe_fd(-1, 0);
            (void) execlp(COMPRESS_PATH, COMPRESS_PATH, flags->comp_type, (char *)0);
-           error("could not exec %s: %s", COMPRESS_PATH, strerror(errno));
+           error(_("could not exec %s: %s"), COMPRESS_PATH, strerror(errno));
+           /*NOTREACHED*/
        }
     } else if(need_uncompress) {
         /*
@@ -911,129 +1017,136 @@ rst_flags_t *flags;
         */ 
        switch(myout->comp_enc_pid = fork()) {
        case -1: 
-           error("could not fork for %s: %s",
+           error(_("could not fork for %s: %s"),
                  UNCOMPRESS_PATH, strerror(errno));
+           /*NOTREACHED*/
+
        default:
            aclose(pipes[stage].pipe[0]);
            aclose(pipes[stage+1].pipe[1]);
             stage++;
            break;
+
        case 0:
-           if(dup2(pipes[stage].pipe[0], 0) == -1)
-               error("error uncompress stdin [dup2 %d %d: %s]", stage,
+           if(dup2(pipes[stage].pipe[0], 0) == -1) {
+               error(_("error uncompress stdin [dup2 %d %d: %s]"), stage,
                  pipes[stage].pipe[0], strerror(errno));
+               /*NOTREACHED*/
+           }
 
-           if(dup2(pipes[stage+1].pipe[1], 1) == -1)
-               error("error uncompress stdout [dup2 %d %d: %s]", stage + 1,
+           if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
+               error(_("error uncompress stdout [dup2 %d %d: %s]"), stage + 1,
                  pipes[stage+1].pipe[1], strerror(errno));
+               /*NOTREACHED*/
+           }
 
            safe_fd(-1, 0);
-           if (*file->srvcompprog) {
-             (void) execlp(file->srvcompprog, file->srvcompprog, "-d", NULL);
-             error("could not exec %s: %s", file->srvcompprog, strerror(errno));
-           } else if (*file->clntcompprog) {
-             (void) execlp(file->clntcompprog, file->clntcompprog, "-d", NULL);
-             error("could not exec %s: %s", file->clntcompprog, strerror(errno));
+           if (source->header->srvcompprog[0] != '\0') {
+             (void) execlp(source->header->srvcompprog,
+                            source->header->srvcompprog, "-d", NULL);
+             error("could not exec %s: %s", source->header->srvcompprog,
+                    strerror(errno));
+              g_assert_not_reached();
+           } else if (source->header->clntcompprog[0] != '\0') {
+             (void) execlp(source->header->clntcompprog,
+                            source->header->clntcompprog, "-d", NULL);
+             error("could not exec %s: %s", source->header->clntcompprog,
+                    strerror(errno));
+              g_assert_not_reached();
            } else {
              (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
 #ifdef UNCOMPRESS_OPT
                          UNCOMPRESS_OPT,
 #endif
-                         (char *)0);
-             error("could not exec %s: %s", UNCOMPRESS_PATH, strerror(errno));
+                         (char *)NULL);
+             error(_("could not exec %s: %s"), UNCOMPRESS_PATH, strerror(errno));
+             /*NOTREACHED*/
            }
        }
     }
 
     /* copy the rest of the file from tape to the output */
-    if(flags->blocksize > 0)
-       blocksize = flags->blocksize;
-    else if(blocksize == -1)
-       blocksize = DISK_BLOCK_BYTES;
-    buffer = alloc(blocksize);
-
-    do {
-       bytes_read = get_block(tapefd, buffer, isafile);
-       if(bytes_read < 0) {
-           error("restore read error: %s", strerror(errno));
-           /* NOTREACHED */
-       }
-
-       if(bytes_read > 0) {
-           if((s = fullwrite(pipes[0].pipe[1], buffer, bytes_read)) < 0) {
-               if ((errno == EPIPE) || (errno == ECONNRESET)) {
-                   /*
-                    * reading program has ended early
-                    * e.g: bzip2 closes pipe when it
-                    * trailing garbage after EOF
-                    */
-                   break;
-               }
-               perror("restore: write error");
-               exit(2);
-           }
-       }
-       else if(isafile) {
+    if (source->restore_mode == HOLDING_MODE) {
+        dumpfile_t file;
+       queue_fd_t queue_read = {source->u.holding_fd, NULL};
+       queue_fd_t queue_write = {pipes[0].pipe[1], NULL};
+        memcpy(& file, source->header, sizeof(file));
+        for (;;) {
+            do_consumer_producer_queue(fd_read_producer,
+                                       &queue_read,
+                                       fd_write_consumer,
+                                       &queue_write);
+           /* TODO: Check error */
            /*
             * See if we need to switch to the next file in a holding restore
             */
-           if(file->cont_filename[0] == '\0') {
+           if(file.cont_filename[0] == '\0') {
                break;                          /* no more files */
            }
-           aclose(tapefd);
-           if((tapefd = open(file->cont_filename, O_RDONLY)) == -1) {
-               char *cont_filename = strrchr(file->cont_filename,'/');
+           aclose(queue_read.fd);
+           if((queue_read.fd = open(file.cont_filename, O_RDONLY)) == -1) {
+               char *cont_filename =
+                    strrchr(file.cont_filename,'/');
                if(cont_filename) {
                    cont_filename++;
-                   if((tapefd = open(cont_filename,O_RDONLY)) == -1) {
-                       error("can't open %s: %s", file->cont_filename,
+                   if((queue_read.fd = open(cont_filename,O_RDONLY)) == -1) {
+                       error(_("can't open %s: %s"), file.cont_filename,
                              strerror(errno));
+                       /*NOTREACHED*/
                    }
                    else {
-                       fprintf(stderr, "cannot open %s: %s\n",
-                               file->cont_filename, strerror(errno));
-                       fprintf(stderr, "using %s\n",
+                       g_fprintf(stderr, _("cannot open %s: %s\n"),
+                               file.cont_filename, strerror(errno));
+                       g_fprintf(stderr, _("using %s\n"),
                                cont_filename);
                    }
                }
                else {
-                   error("can't open %s: %s", file->cont_filename,
+                   error(_("can't open %s: %s"), file.cont_filename,
                          strerror(errno));
+                   /*NOTREACHED*/
                }
            }
-           read_file_header(file, tapefd, isafile, flags);
-           if(file->type != F_DUMPFILE && file->type != F_CONT_DUMPFILE
-                   && file->type != F_SPLIT_DUMPFILE) {
-               fprintf(stderr, "unexpected header type: ");
-               print_header(stderr, file);
+           read_holding_disk_header(&file, queue_read.fd, flags);
+           if(file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE
+                   && file.type != F_SPLIT_DUMPFILE) {
+               g_fprintf(stderr, _("unexpected header type: "));
+               print_header(stderr, source->header);
                exit(2);
            }
-       }
-    } while (bytes_read > 0);
-
-    amfree(buffer);
+       }            
+    } else {
+       queue_fd_t queue_fd = {pipes[0].pipe[1], NULL};
+        device_read_to_fd(source->u.device, &queue_fd);
+       /* TODO: Check error */
+    }
 
+    amfree(free_myout);
     if(!flags->inline_assemble) {
         if(out != dest)
            aclose(out);
     }
     if(!is_continuation){
        if(tmp_filename && stat(tmp_filename, &statinfo) < 0){
-           error("Can't stat the file I just created (%s)!\n", tmp_filename);
+           error(_("Can't stat the file I just created (%s)!"), tmp_filename);
+           /*NOTREACHED*/
+       } else {
+           statinfo.st_size = (off_t)0;
        }
-       if(check_for_aborted){
+       if (check_for_aborted && final_filename) {
            char *old_dump = final_filename;
            struct stat oldstat;
            if(stat(old_dump, &oldstat) >= 0){
                if(oldstat.st_size <= statinfo.st_size){
                    dumplist_t *prev_fileentry = NULL;
                    open_output_t *prev_out = NULL;
-                   fprintf(stderr, "Newer restore is larger, using that\n");
+                   g_fprintf(stderr, _("Newer restore is larger, using that\n"));
                    /* nuke the old dump's entry in alldump_list */
                    for(fileentry=alldumps_list;
                            fileentry->next;
                            fileentry=fileentry->next){
-                       if(headers_equal(file, fileentry->file, 0)){
+                       if(headers_equal(source->header,
+                                         fileentry->file, 0)){
                            if(prev_fileentry){
                                prev_fileentry->next = fileentry->next;
                            }
@@ -1047,7 +1160,7 @@ rst_flags_t *flags;
                    }
                    myout = open_outputs;
                    while(myout != NULL){
-                       if(headers_equal(file, myout->file, 0)){
+                       if(headers_equal(source->header, myout->file, 0)){
                            if(myout->outfd >= 0)
                                aclose(myout->outfd);
                            if(prev_out){
@@ -1062,24 +1175,27 @@ rst_flags_t *flags;
                    }
                }
                else{
-                   fprintf(stderr, "Older restore is larger, using that\n");
-                   unlink(tmp_filename);
+                   g_fprintf(stderr, _("Older restore is larger, using that\n"));
+                   if (tmp_filename)
+                       unlink(tmp_filename);
                    amfree(tempdump->file);
                    amfree(tempdump);
                    amfree(tmp_filename);
                    amfree(final_filename);
-                    return (bytes_read);
+                    amfree(filename);
+                    return;
                }
            }
        }
        if(tmp_filename && final_filename &&
-               rename(tmp_filename, final_filename) < 0){
-           error("Can't rename %s to %s: %s\n", tmp_filename, final_filename,
-                                            strerror(errno));
+           rename(tmp_filename, final_filename) < 0) {
+           error(_("Can't rename %s to %s: %s"),
+                  tmp_filename, final_filename, strerror(errno));
+           /*NOTREACHED*/
        }
     }
-    if(tmp_filename) amfree(tmp_filename);
-    if(final_filename) amfree(final_filename);
+    amfree(tmp_filename);
+    amfree(final_filename);
 
 
     /*
@@ -1087,28 +1203,831 @@ rst_flags_t *flags;
      * structures (we waited in case we needed to give up)
      */
     if(!is_continuation){
-        oldout = alloc(sizeof(open_output_t));
-        oldout->file = alloc(sizeof(dumpfile_t));
-        memcpy(oldout->file, file, sizeof(dumpfile_t));
+        oldout = alloc(SIZEOF(open_output_t));
+        oldout->file = alloc(SIZEOF(dumpfile_t));
+        memcpy(oldout->file, source->header, SIZEOF(dumpfile_t));
         if(flags->inline_assemble) oldout->outfd = pipes[0].pipe[1];
        else oldout->outfd = -1;
         oldout->comp_enc_pid = -1;
-        oldout->lastpartnum = file->partnum;
+        oldout->lastpartnum = source->header->partnum;
         oldout->next = open_outputs;
         open_outputs = oldout;
     }
     if(alldumps_list){
-       for(fileentry=alldumps_list;fileentry->next;fileentry=fileentry->next);
+       fileentry = alldumps_list;
+       while (fileentry->next != NULL)
+           fileentry=fileentry->next;
        fileentry->next = tempdump;
     }
     else {
        alldumps_list = tempdump;
     }
+}
+
+gboolean
+set_restore_device_read_buffer_size(
+    Device *device,
+    rst_flags_t *flags)
+{
+    /* if the user specified a blocksize, try to use it */
+    if (flags->blocksize) {
+       GValue val;
+       gboolean success;
+
+       bzero(&val, sizeof(GValue));
+
+       g_value_init(&val, G_TYPE_UINT);
+       g_value_set_uint(&val, flags->blocksize);
+       success = device_property_set(device, PROPERTY_READ_BUFFER_SIZE, &val);
+       g_value_unset(&val);
+       if (!success) {
+           if (device->status == DEVICE_STATUS_SUCCESS) {
+               /* device doesn't have this property, so quietly ignore it */
+               g_warning(_("Device %s does not support PROPERTY_READ_BUFFER_SIZE; ignoring block size %zd"),
+                       device->device_name, flags->blocksize);
+           } else {
+               /* it's a real error */
+               return FALSE;
+           }
+       }
+    }
+
+    return TRUE;
+}
+
+/* return NULL if the label is not the expected one                     */
+/* returns a Device handle if it is the expected one. */
+/* FIXME: Was label_of_current_slot */
+static Device *
+conditional_device_open(char         *tapedev,
+                        FILE         *prompt_out,
+                        rst_flags_t  *flags,
+                        am_feature_t *their_features,
+                        tapelist_t   *desired_tape)
+{
+    Device * rval;
+
+    if (tapedev == NULL) {
+       send_message(prompt_out, flags, their_features,
+                    _("Volume labeled '%s' not found."), desired_tape->label);
+        return NULL;
+    }
+
+    rval = device_open(tapedev);
+    g_assert(rval != NULL);
+    if (rval->status != DEVICE_STATUS_SUCCESS) {
+       send_message(prompt_out, flags, their_features, 
+                    "Error opening device '%s': %s.",
+                    tapedev, device_error(rval));
+        g_object_unref(rval);
+        return NULL;
+    }
+
+    if (!device_configure(rval, TRUE)) {
+        g_fprintf(stderr, "Error configuring device: %s\n", device_error_or_status(rval));
+        g_object_unref(rval);
+        return NULL;
+    }
+
+    if (!set_restore_device_read_buffer_size(rval, flags)) {
+       send_message(prompt_out, flags, their_features,
+                    "Error setting read block size on '%s': %s.",
+                    tapedev, device_error(rval));
+        g_object_unref(rval);
+        return NULL;
+    }
+    device_read_label(rval);
+
+    if (rval->volume_label == NULL) {
+       char *errstr = stralloc2("Not an amanda tape: ",
+                                device_error(rval));
+        send_message(prompt_out, flags, their_features, "%s", errstr);
+       amfree(errstr);
+        g_object_unref(rval);
+        return NULL;
+    }
+
+    if (!device_start(rval, ACCESS_READ, NULL, NULL)) {
+        send_message(prompt_out, flags, their_features,
+                     "Colud not open device %s for reading: %s.\n",
+                     tapedev, device_error(rval));
+        return NULL;
+    }
 
-    return (bytes_read);
+    if (flags->check_labels && desired_tape &&
+        strcmp(rval->volume_label, desired_tape->label) != 0) {
+        send_message(prompt_out, flags, their_features,
+                     "Label mismatch, got %s and expected %s",
+                     rval->volume_label, desired_tape->label);
+        g_object_unref(rval);
+        return NULL;
+    }
+
+    return rval;
 }
 
+/* Do the right thing to try and load the next required tape. See
+   LoadStatus above for return value meaning. */
+LoadStatus
+load_next_tape(
+    char         **cur_tapedev,
+    FILE          *prompt_out,
+    int            backwards,
+    rst_flags_t   *flags,
+    am_feature_t  *their_features,
+    tapelist_t    *desired_tape)
+{
+    if (desired_tape) {
+       send_message(prompt_out, flags, their_features,
+                    _("Looking for tape %s..."),
+                    desired_tape->label);
+       if (backwards) {
+            loadlabel_data data;
+            data.cur_tapedev = cur_tapedev;
+            data.searchlabel = desired_tape->label;
+           data.flags = flags;
+           changer_find(&data, scan_init, loadlabel_slot,
+                        desired_tape->label);
+            return LOAD_CHANGER;
+       } else {
+           amfree(curslot);
+           changer_loadslot("next", &curslot,
+                            cur_tapedev);
+            return LOAD_NEXT;
+       }
+    } else {
+       assert(!flags->amidxtaped);
+       amfree(curslot);
+       changer_loadslot("next", &curslot, cur_tapedev);
+        return LOAD_NEXT;
+    }
 
+    g_assert_not_reached();
+}
+
+
+/* will never return LOAD_CHANGER. */
+LoadStatus
+load_manual_tape(
+    char         **tapedev_ptr,
+    FILE          *prompt_out,
+    FILE          *prompt_in,
+    rst_flags_t   *flags,
+    am_feature_t  *their_features,
+    tapelist_t    *desired_tape)
+{
+    char *input = NULL;
+
+    if (flags->amidxtaped) {
+       if (their_features &&
+           am_has_feature(their_features,
+                          fe_amrecover_FEEDME)) {
+           g_fprintf(prompt_out, "FEEDME %s\r\n",
+                   desired_tape->label);
+           fflush(prompt_out);
+           input = agets(prompt_in);/* Strips \n but not \r */
+           if(!input) {
+               error(_("Connection lost with amrecover"));
+               /*NOTREACHED*/
+           } else if (strcmp("OK\r", input) == 0) {
+           } else if (strncmp("TAPE ", input, 5) == 0) {
+               amfree(*tapedev_ptr);
+               *tapedev_ptr = alloc(1025);
+               if (sscanf(input, "TAPE %1024s\r", *tapedev_ptr) != 1) {
+                   error(_("Got bad response from amrecover: %s"), input);
+                   /*NOTREACHED*/
+               }
+           } else {
+               send_message(prompt_out, flags, their_features,
+                            _("Got bad response from amrecover: %s"), input);
+               error(_("Got bad response from amrecover: %s"), input);
+               /*NOTREACHED*/
+           }
+       } else {
+           send_message(prompt_out, flags, their_features,
+                        _("Client doesn't support fe_amrecover_FEEDME"));
+           error(_("Client doesn't support fe_amrecover_FEEDME"));
+           /*NOTREACHED*/
+       }
+    }
+    else {
+       if (desired_tape) {
+           g_fprintf(prompt_out,
+                   _("Insert tape labeled %s in device %s \n"
+                   "and press enter, ^D to finish reading tapes\n"),
+                   desired_tape->label, *tapedev_ptr);
+       } else {
+           g_fprintf(prompt_out,_("Insert a tape to search and press "
+                   "enter, ^D to finish reading tapes\n"));
+       }
+       fflush(prompt_out);
+       if((input = agets(prompt_in)) == NULL)
+            return LOAD_STOP;
+    }
+
+    amfree(input);
+    return LOAD_NEXT;
+}
+
+/* Search a seen-tapes list for a particular name, to see if we've already
+ * processed this tape. Returns TRUE if this label has already been seen. */
+static gboolean check_volume_seen(seentapes_t * list, char * label) {
+    seentapes_t * cur_tape;
+    for (cur_tape = list; cur_tape != NULL; cur_tape = cur_tape->next) {
+        if (strcmp(cur_tape->label, label) == 0) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/* Add a volume to the seen tapes list. */
+static void record_seen_volume(seentapes_t ** list, char * label,
+                               char * slotstr) {
+    seentapes_t * new_entry;
+
+    if (list == NULL)
+        return;
+
+    new_entry = malloc(sizeof(seentapes_t));
+    new_entry->label = stralloc(label);
+    if (slotstr == NULL) {
+        new_entry->slotstr = NULL;
+    } else {
+        new_entry->slotstr = stralloc(slotstr);
+    }
+    new_entry->files = NULL;
+    new_entry->next = *list;
+    *list = new_entry;
+}
+
+/* Record a specific dump on a volume. */
+static void record_seen_dump(seentapes_t * volume, dumpfile_t * header) {
+    dumplist_t * this_dump;
+
+    if (volume == NULL)
+        return;
+    
+    this_dump = malloc(sizeof(*this_dump));
+    this_dump->file = g_memdup(header, sizeof(*header));
+    this_dump->next = NULL;
+    if (volume->files) {
+        dumplist_t * tmp_dump = volume->files;
+        while (tmp_dump->next != NULL) {
+            tmp_dump = tmp_dump->next;
+        }
+        tmp_dump->next = this_dump;
+    } else {
+        volume->files = this_dump;
+    }
+}
+
+static void print_tape_inventory(FILE * logstream, seentapes_t * tape_seen,
+                                 char * timestamp, char * label,
+                                 int tape_count) {
+    char * logline;
+    dumplist_t * fileentry;
+
+    logline = log_genstring(L_START, "taper",
+                            "datestamp %s label %s tape %d",
+                            timestamp, label, tape_count);
+    fputs(logline, logstream);
+    amfree(logline);
+    for(fileentry=tape_seen->files; fileentry; fileentry=fileentry->next){
+        switch (fileentry->file->type) {
+        case F_DUMPFILE:
+            logline = log_genstring(L_SUCCESS, "taper",
+                                    "%s %s %s %d [faked log entry]",
+                                    fileentry->file->name,
+                                    fileentry->file->disk,
+                                    fileentry->file->datestamp,
+                                    fileentry->file->dumplevel);
+            break;
+        case F_SPLIT_DUMPFILE:
+            logline = log_genstring(L_CHUNK, "taper", 
+                                    "%s %s %s %d %d [faked log entry]",
+                                    fileentry->file->name,
+                                    fileentry->file->disk,
+                                    fileentry->file->datestamp,
+                                    fileentry->file->partnum,
+                                    fileentry->file->dumplevel);
+            break;
+        default:
+            break;
+        }
+        if(logline != NULL){
+            fputs(logline, logstream);
+            amfree(logline);
+            fflush(logstream);
+        }
+    }
+}
+
+/* Check if the given header matches the given dumpspecs. Returns
+   TRUE if dumpspecs is NULL and false if the header is NULL. Returns
+   true if the header matches the  match list. */
+static gboolean run_dumpspecs(GSList * dumpspecs,
+                               dumpfile_t * header) {
+    dumpspec_t *ds;
+
+    if (dumpspecs == NULL)
+        return TRUE;
+    if (header == NULL)
+        return FALSE;
+
+    while (dumpspecs) {
+       ds = (dumpspec_t *)dumpspecs->data;
+        if (disk_match(header, ds->datestamp, ds->host,
+                       ds->disk, ds->level) != 0) {
+            return TRUE;
+        }
+       dumpspecs = dumpspecs->next;
+    }
+
+    return FALSE;
+}
+
+/* A wrapper around restore() above. This function does some extra
+   checking to seek to the file in question and ensure that we really,
+   really want to use it.
+
+   The next_file argument provides instruction on what to do if the
+   requested file does not exist on the volume: If next_file is NULL
+   then if the requested file is missing the function will return
+   RESTORE_STATUS_NEXT_FILE. If next_file is not NULL then the first
+   extant file whose number is equal to or greater than file_num will
+   be attempted. *next_file will be filled in with the number of the
+   file following the one that was attempted. */
+static RestoreFileStatus
+try_restore_single_file(Device * device, int file_num, int* next_file,
+                        FILE * prompt_out,
+                        rst_flags_t * flags,
+                        am_feature_t * their_features,
+                        dumpfile_t * first_restored_file,
+                        GSList * dumpspecs,
+                        seentapes_t * tape_seen) {
+    RestoreSource source;
+    source.u.device = device;
+    source.restore_mode = DEVICE_MODE;
+
+    source.header = device_seek_file(device, file_num);
+
+    if (source.header == NULL) {
+        /* This definitely indicates an error. */
+        send_message(prompt_out, flags, their_features,
+                     "Could not seek device %s to file %d: %s.",
+                     device->device_name, file_num,
+                    device_error(device));
+        return RESTORE_STATUS_NEXT_TAPE;
+    } else if (source.header->type == F_TAPEEND) {
+        amfree(source.header);
+        return RESTORE_STATUS_NEXT_TAPE;
+    } else if (device->file != file_num) {
+        if (next_file == NULL) {
+            send_message(prompt_out, flags, their_features,
+                         "Requested file %d does not exist.",
+                         file_num);
+            return RESTORE_STATUS_NEXT_FILE;
+        } else {
+            send_message(prompt_out, flags, their_features,
+                         "Skipped from file %d to file %d.", 
+                         file_num, device->file);
+            file_num = device->file;
+        }
+    }
+    if (!am_has_feature(their_features, fe_amrecover_dle_in_header)) {
+       source.header->dle_str = NULL;
+    }
+
+    if (next_file != NULL) {
+        *next_file = file_num + 1;
+    }
+    
+    g_return_val_if_fail(source.header->type == F_DUMPFILE ||
+                         source.header->type == F_CONT_DUMPFILE ||
+                         source.header->type == F_SPLIT_DUMPFILE,
+                         RESTORE_STATUS_NEXT_FILE);
+    
+
+    if (!run_dumpspecs(dumpspecs, source.header)) {
+       if(!flags->amidxtaped) {
+            g_fprintf(prompt_out, "%s: %d: skipping ",
+                   get_pname(), file_num);
+            print_header(prompt_out, source.header);
+       }
+        return RESTORE_STATUS_NEXT_FILE;
+    }
+
+    if (first_restored_file != NULL &&
+        first_restored_file->type != F_UNKNOWN &&
+       first_restored_file->type != F_EMPTY &&
+        !headers_equal(first_restored_file, source.header, 1) &&
+        (flags->pipe_to_fd == fileno(stdout))) {
+        return RESTORE_STATUS_STOP;
+    }
+
+    if (!flags->amidxtaped) {
+       g_fprintf(stderr, "%s: %d: restoring ",
+               get_pname(), file_num);
+       print_header(stderr, source.header);
+    }
+    record_seen_dump(tape_seen, source.header);
+    restore(&source, flags);
+    if (first_restored_file) {
+       memcpy(first_restored_file, source.header, sizeof(dumpfile_t));
+    }
+    return RESTORE_STATUS_NEXT_FILE;
+}
+
+/* This function handles processing of a particular tape or holding
+   disk file. It returns TRUE if it is useful to load another tape.*/
+
+gboolean
+search_a_tape(Device      * device,
+              FILE         *prompt_out, /* Where to send any prompts */
+              rst_flags_t  *flags,      /* Restore options. */
+              am_feature_t *their_features, 
+              tapelist_t   *desired_tape, /* A list of desired tape files */
+              GSList *dumpspecs, /* What disks to restore. */
+              seentapes_t **tape_seen,  /* Where to record data on
+                                           this tape. */
+              /* May be NULL. If zeroed, will be filled in with the
+                 first restored file. If already filled in, then we
+                 may only restore other files from the same dump. */
+              dumpfile_t   * first_restored_file,
+              int           tape_count,
+              FILE * logstream) {
+    seentapes_t * tape_seen_head = NULL;
+    RestoreSource source;
+    off_t       filenum;
+
+    int         tapefile_idx = -1;
+    int         i;
+    RestoreFileStatus restore_status = RESTORE_STATUS_NEXT_TAPE;
+
+    /* if we're doing an inventory (logstream != NULL), then we need
+     * somewhere to keep track of our seen tapes */
+    g_assert(tape_seen != NULL || logstream == NULL);
+
+    source.restore_mode = DEVICE_MODE;
+    source.u.device = device;
+
+    filenum = (off_t)0;
+    if(desired_tape && desired_tape->numfiles > 0)
+       tapefile_idx = 0;
+
+    if (desired_tape) {
+       dbprintf(_("search_a_tape: desired_tape=%p label=%s\n"),
+                 desired_tape, desired_tape->label);
+       dbprintf(_("tape:   numfiles = %d\n"), desired_tape->numfiles);
+       for (i=0; i < desired_tape->numfiles; i++) {
+           dbprintf(_("tape:   files[%d] = %lld\n"),
+                     i, (long long)desired_tape->files[i]);
+       }
+    } else {
+       dbprintf(_("search_a_tape: no desired_tape\n"));
+    }
+    dbprintf(_("current tapefile_idx = %d\n"), tapefile_idx);
+
+    if (tape_seen) {
+        if (check_volume_seen(*tape_seen, device->volume_label)) {
+            send_message(prompt_out, flags, their_features,
+                         "Skipping repeat tape %s in slot %s",
+                         device->volume_label, curslot);
+            return TRUE;
+        }
+        record_seen_volume(tape_seen, device->volume_label, curslot);
+        tape_seen_head = *tape_seen;
+    }
+       
+    if (desired_tape && desired_tape->numfiles > 0) {
+        /* Iterate the tape list, handle each file in order. */
+        int file_index;
+        for (file_index = 0; file_index < desired_tape->numfiles;
+             file_index ++) {
+            int file_num = desired_tape->files[file_index];
+            restore_status = try_restore_single_file(device, file_num, NULL,
+                                                     prompt_out, flags,
+                                                     their_features,
+                                                     first_restored_file,
+                                                     NULL, tape_seen_head);
+            if (restore_status != RESTORE_STATUS_NEXT_FILE)
+                break;
+        }
+    } else if(flags->fsf && flags->amidxtaped) {
+        /* Restore a single file, then quit. */
+        restore_status =
+            try_restore_single_file(device, flags->fsf, NULL, prompt_out, flags,
+                                    their_features, first_restored_file,
+                                    dumpspecs, tape_seen_head);
+    } else {
+        /* Search the tape from beginning to end. */
+        int file_num;
+
+        if (flags->fsf > 0) {
+            file_num = flags->fsf;
+        } else {
+            file_num = 1;
+        }
+
+       if (!flags->amidxtaped) {
+            g_fprintf(prompt_out, "Restoring from tape %s starting with file %d.\n",
+                   device->volume_label, file_num);
+           fflush(prompt_out);
+       }
+
+        for (;;) {
+            restore_status =
+                try_restore_single_file(device, file_num, &file_num,
+                                        prompt_out, flags,
+                                        their_features, first_restored_file,
+                                        dumpspecs, tape_seen_head);
+            if (restore_status != RESTORE_STATUS_NEXT_FILE)
+                break;
+        }
+    }
+    
+    /* spit out our accumulated list of dumps, if we're inventorying */
+    if (logstream != NULL) {
+        print_tape_inventory(logstream, tape_seen_head, device->volume_time,
+                             device->volume_label, tape_count);
+    }
+    return (restore_status != RESTORE_STATUS_STOP);
+}
+
+static void free_seen_tapes(seentapes_t * seentapes) {
+    while (seentapes != NULL) {
+       seentapes_t *tape_seen = seentapes;
+       seentapes = seentapes->next;
+       while(tape_seen->files != NULL) {
+           dumplist_t *temp_dump = tape_seen->files;
+           tape_seen->files = temp_dump->next;
+           amfree(temp_dump->file);
+           amfree(temp_dump);
+       }
+       amfree(tape_seen->label);
+       amfree(tape_seen->slotstr);
+       amfree(tape_seen);
+       
+    }
+}
+
+/* Spit out a list of expected tapes, so people with manual changers know
+   what to load */
+static void print_expected_tape_list(FILE* prompt_out, FILE* prompt_in,
+                                     tapelist_t *tapelist,
+                                     rst_flags_t * flags) {
+    tapelist_t * cur_volume;
+
+    g_fprintf(prompt_out, "The following tapes are needed:");
+    for(cur_volume = tapelist; cur_volume != NULL;
+        cur_volume = cur_volume->next){
+       g_fprintf(prompt_out, " %s", cur_volume->label);
+    }
+    g_fprintf(prompt_out, "\n");
+    fflush(prompt_out);
+    if(flags->wait_tape_prompt){
+       char *input = NULL;
+        g_fprintf(prompt_out,"Press enter when ready\n");
+       fflush(prompt_out);
+        input = agets(prompt_in);
+       amfree(input);
+       g_fprintf(prompt_out, "\n");
+       fflush(prompt_out);
+    }
+}
+
+/* Restore a single holding-disk file. We will fill in this_header
+   with the header from this restore (if it is not null), and in the
+   stdout-pipe case, we abort according to last_header. Returns TRUE
+   if the restore should continue, FALSE if we are done. */
+gboolean restore_holding_disk(FILE * prompt_out,
+                              rst_flags_t * flags,
+                              am_feature_t * features,
+                              tapelist_t * file,
+                              seentapes_t ** seen,
+                              GSList * dumpspecs,
+                              dumpfile_t * this_header,
+                              dumpfile_t * last_header) {
+    RestoreSource source;
+    gboolean read_result;
+    dumpfile_t header;
+
+    source.header = &header;
+    source.restore_mode = HOLDING_MODE;
+    source.u.holding_fd = robust_open(file->label, 0, 0);
+    if (source.u.holding_fd < 0) {
+        send_message(prompt_out, flags, features, 
+                     "could not open %s: %s",
+                     file->label, strerror(errno));
+        return TRUE;
+    }
+
+    g_fprintf(stderr, "Reading %s from fd %d\n",
+            file->label, source.u.holding_fd);
+    
+    read_result = read_holding_disk_header(source.header,
+                                           source.u.holding_fd, flags);
+    if (!read_result) {
+        send_message(prompt_out, flags, features, 
+                     "Invalid header reading %s.",
+                     file->label);
+        aclose(source.u.holding_fd);
+        return TRUE;
+    }
+
+    if (!run_dumpspecs(dumpspecs, source.header)) {
+        return FALSE;
+    }
+
+    if (last_header != NULL && !flags->amidxtaped &&
+       flags->pipe_to_fd == STDOUT_FILENO &&
+       last_header->type != F_UNKNOWN &&
+        !headers_equal(last_header, source.header, 1)) {
+        return FALSE;
+    } else if (this_header != NULL) {
+        memcpy(this_header, source.header, sizeof(*this_header));
+    }
+
+    if (seen != NULL) {
+        record_seen_volume(seen, file->label, "<none>");
+        record_seen_dump(*seen, source.header);
+    }
+
+    print_header(stderr, source.header);
+
+    restore(&source, flags);
+    aclose(source.u.holding_fd);
+    return TRUE;
+}
+
+/* Ask for a specific manual tape. If we find the right one, then open it
+ * and return a Device handle. If not, return NULL. Pass a device name, but
+ * it might be overridden. */
+static Device* manual_find_tape(char ** cur_tapedev, tapelist_t * cur_volume,
+                                FILE * prompt_out, FILE * prompt_in,
+                                rst_flags_t * flags,
+                                am_feature_t * features) {
+    LoadStatus status = LOAD_NEXT;
+    Device * rval;
+
+    for (;;) {
+        status = load_manual_tape(cur_tapedev, prompt_out, prompt_in,
+                                  flags, features, cur_volume);
+        
+        if (status == LOAD_STOP)
+            return NULL;
+
+        rval =  conditional_device_open(*cur_tapedev, prompt_out, flags,
+                                        features, cur_volume);
+        if (rval != NULL)
+            return rval;
+    }
+}
+
+/* If we have a tapelist, then we mandate restoring in tapelist
+   order. The logic is simple: Get the next tape, and deal with it,
+   then move on to the next one. */
+static void
+restore_from_tapelist(FILE * prompt_out,
+                      FILE * prompt_in,
+                      tapelist_t * tapelist,
+                      GSList * dumpspecs,
+                      rst_flags_t * flags,
+                      am_feature_t * features,
+                      char * cur_tapedev,
+                      gboolean use_changer,
+                      FILE * logstream) {
+    tapelist_t * cur_volume;
+    dumpfile_t first_restored_file;
+    seentapes_t * seentapes = NULL;
+
+    fh_init(&first_restored_file);
+
+    for(cur_volume = tapelist; cur_volume != NULL;
+        cur_volume = cur_volume->next){
+        if (cur_volume->isafile) {
+            /* Restore from holding disk; just go. */
+            if (first_restored_file.type == F_UNKNOWN) {
+                if (!restore_holding_disk(prompt_out, flags,
+                                          features, cur_volume, &seentapes,
+                                          NULL, NULL, &first_restored_file)) {
+                    break;
+                }
+            } else {
+                restore_holding_disk(prompt_out, flags, features,
+                                     cur_volume, &seentapes,
+                                     NULL, &first_restored_file, NULL);
+            }
+           if (flags->pipe_to_fd == fileno(stdout)) {
+               break;
+           }
+        } else {
+            Device * device = NULL;
+            if (use_changer) {
+                char * tapedev = NULL;
+                loadlabel_data data;
+                data.cur_tapedev = &tapedev;
+                data.searchlabel =  cur_volume->label;
+               data.flags = flags;
+                changer_find(&data, scan_init, loadlabel_slot,
+                             cur_volume->label);
+                device = conditional_device_open(tapedev, prompt_out,
+                                                 flags, features,
+                                                 cur_volume);
+               amfree(tapedev);
+            }
+
+            if (device == NULL)
+                device = manual_find_tape(&cur_tapedev, cur_volume, prompt_out,
+                                          prompt_in, flags, features);
+
+            if (device == NULL)
+                break;
+
+            if (use_changer) {
+                g_fprintf(stderr, "Scanning volume %s (slot %s)\n",
+                          device->volume_label,
+                          curslot);
+            } else {
+                g_fprintf(stderr, "Scanning volume %s\n",
+                          device->volume_label);
+            }
+
+            if (!search_a_tape(device, prompt_out, flags, features,
+                               cur_volume, dumpspecs, &seentapes,
+                               &first_restored_file, 0, logstream)) {
+                g_object_unref(device);
+                break;
+            }
+            g_object_unref(device);
+        }            
+    }
+
+    free_seen_tapes(seentapes);
+}
+
+/* This function works when we are operating without a tapelist
+   (regardless of whether or not we have a changer). This only happens
+   when we are using amfetchdump without dump logs, but in the future
+   may include amrestore as well. The philosophy is to keep loading
+   tapes until we run out. */
+static void
+restore_without_tapelist(FILE * prompt_out,
+                         FILE * prompt_in,
+                         GSList * dumpspecs,
+                         rst_flags_t * flags,
+                         am_feature_t * features,
+                         char * cur_tapedev,
+                         /* -1 if no changer. */
+                         int slot_count,
+                         FILE * logstream) {
+    int cur_slot = 1;
+    seentapes_t * seentapes;
+    int tape_count = 0;
+    dumpfile_t first_restored_file;
+
+    fh_init(&first_restored_file);
+
+    /* This loop also aborts if we run out of manual tapes, or
+       encounter a changer error. */
+    for (;;) {
+        Device * device = NULL;
+        if (slot_count > 0) {
+            while (cur_slot < slot_count && device == NULL) {
+                amfree(curslot);
+                changer_loadslot("next", &curslot, &cur_tapedev);
+                device = conditional_device_open(cur_tapedev, prompt_out,
+                                                 flags, features,
+                                                 NULL);
+               amfree(cur_tapedev);
+                cur_slot ++;
+            }
+            if (cur_slot >= slot_count)
+                break;
+        } else {
+            device = manual_find_tape(&cur_tapedev, NULL, prompt_out,
+                                      prompt_in, flags, features);
+        }
+        
+        if (device == NULL)
+            break;
+
+       g_fprintf(stderr, "Scanning %s (slot %s)\n", device->volume_label,
+               curslot);
+        
+        if (!search_a_tape(device, prompt_out, flags, features,
+                           NULL, dumpspecs, &seentapes, &first_restored_file,
+                           tape_count, logstream)) {
+            g_object_unref(device);
+            break;
+        }
+        g_object_unref(device);
+        tape_count ++;
+    }
+           
+    free_seen_tapes(seentapes);
+}
 
 /* 
  * Take a pattern of dumps and restore it blind, a la amrestore.  In addition,
@@ -1118,39 +2037,26 @@ rst_flags_t *flags;
  * tapes to search (rather than "everything I can find"), which in turn can
  * optionally list specific files to restore.
  */
-void search_tapes(prompt_out, use_changer, tapelist, match_list, flags, their_features)
-FILE *prompt_out;
-int use_changer;
-tapelist_t *tapelist;
-match_list_t *match_list;
-rst_flags_t *flags;
-am_feature_t *their_features;
+ void
+search_tapes(
+    FILE *             prompt_out,
+    FILE *              prompt_in,
+    int                        use_changer,
+    tapelist_t *       tapelist,
+    GSList *           dumpspecs,
+    rst_flags_t *      flags,
+    am_feature_t *     their_features)
 {
-    struct stat stat_tape;
-    char *err;
-    int have_changer = 1;
-    int slot_num = -1;
+    char *cur_tapedev;
     int slots = -1;
-    int filenum;
     FILE *logstream = NULL;
-    dumplist_t *fileentry = NULL;
     tapelist_t *desired_tape = NULL;
     struct sigaction act, oact;
-    int newtape = 1;
-    ssize_t bytes_read = 0;
 
-    struct seentapes{
-       struct seentapes *next;
-       char *slotstr;
-       char *label;
-       dumplist_t *files;
-    } *seentapes = NULL;
+    device_api_init();
 
     if(!prompt_out) prompt_out = stderr;
 
-    if(flags->blocksize) blocksize = flags->blocksize;
-    else if(blocksize == -1) blocksize = DISK_BLOCK_BYTES;
-
     /* Don't die when child closes pipe */
     signal(SIGPIPE, SIG_IGN);
 
@@ -1159,60 +2065,55 @@ am_feature_t *their_features;
     sigemptyset(&act.sa_mask);
     act.sa_flags = 0;
     if(sigaction(SIGINT, &act, &oact) != 0){
-       error("error setting SIGINT handler: %s", strerror(errno));
+       error(_("error setting SIGINT handler: %s"), strerror(errno));
+       /*NOTREACHED*/
     }
     if(flags->delay_assemble || flags->inline_assemble) exitassemble = 1;
     else exitassemble = 0;
 
     /* if given a log file, print an inventory of stuff found */
-    if(flags->inventory_log){
+    if(flags->inventory_log) {
        if(!strcmp(flags->inventory_log, "-")) logstream = stdout;
-       else if((logstream = fopen(flags->inventory_log, "w+")) == NULL){
-           error("Couldn't open log file %s for writing: %s\n",
+       else if((logstream = fopen(flags->inventory_log, "w+")) == NULL) {
+           error(_("Couldn't open log file %s for writing: %s"),
                  flags->inventory_log, strerror(errno));
+           /*NOTREACHED*/
        }
     }
 
     /* Suss what tape device we're using, whether there's a changer, etc. */
-    if(!use_changer || (have_changer = changer_init()) == 0) {
-       if(flags->alt_tapedev) cur_tapedev = stralloc(flags->alt_tapedev);
-       else if(!cur_tapedev) cur_tapedev = getconf_str(CNF_TAPEDEV);
+    if (use_changer) {
+       use_changer = changer_init();
+    }
+    if (!use_changer) {
+        cur_tapedev = NULL;
+       if (flags->alt_tapedev) {
+           cur_tapedev = stralloc(flags->alt_tapedev);
+       } else if(!cur_tapedev) {
+           cur_tapedev = getconf_str(CNF_TAPEDEV);
+           if (cur_tapedev == NULL) {
+               error(_("No tapedev specified"));
+           }
+       }
        /* XXX oughta complain if no config is loaded */
-       fprintf(stderr, "%s: Using tapedev %s\n", get_pname(), cur_tapedev);
-       have_changer = 0;
-    } else if (have_changer != 1) {
-       error("changer initialization failed: %s", strerror(errno));
+       g_fprintf(stderr, _("%s: Using tapedev %s\n"), get_pname(), cur_tapedev);
     }
     else{ /* good, the changer works, see what it can do */
+       amfree(curslot);
        changer_info(&slots, &curslot, &backwards);
     }
 
-    if(tapelist && !flags->amidxtaped){
-      slots = num_entries(tapelist);
-      /*
-       Spit out a list of expected tapes, so people with manual changers know
-       what to load
-      */
-      fprintf(prompt_out, "The following tapes are needed:");
-      for(desired_tape = tapelist;
-          desired_tape != NULL;
-         desired_tape = desired_tape->next){
-       fprintf(prompt_out, " %s", desired_tape->label);
-      }
-      fprintf(prompt_out, "\n");
-      fflush(prompt_out);
-      if(flags->wait_tape_prompt){
-       char *input = NULL;
-        fprintf(prompt_out,"Press enter when ready\n");
-       fflush(prompt_out);
-        input = agets(stdin);
-       amfree(input);
-       fprintf(prompt_out, "\n");
-       fflush(prompt_out);
-      }
+    if (tapelist && !flags->amidxtaped) {
+        print_expected_tape_list(prompt_out, prompt_in, tapelist, flags);
     }
     desired_tape = tapelist;
 
+    if (use_changer) { /* load current slot */
+       amfree(curslot);
+       cur_tapedev = NULL;
+       changer_loadslot("current", &curslot, &cur_tapedev);
+    }
+
     /*
      * If we're not given a tapelist, iterate over everything our changer can
      * find.  If there's no changer, we'll prompt to be handfed tapes.
@@ -1224,401 +2125,16 @@ am_feature_t *their_features;
      *
      * (obnoxious, isn't this?)
      */
-    slot_num = 0;
-    curslot = stralloc("<none>");
-    while(desired_tape || ((slot_num < slots || !have_changer) && !tapelist)){
-       char *label = NULL;
-       struct seentapes *tape_seen = NULL;
-       dumpfile_t file, tapestart, prev_rst_file;
-       char *logline = NULL;
-       int tapefile_idx = -1;
-       int wrongtape = 0;
-       int isafile = 0;
-
-       /*
-        * Deal with instances where we're being asked to restore from a file
-        */
-       if(desired_tape && desired_tape->isafile){
-           isafile = 1;
-           if ((tapefd = open(desired_tape->label, 0)) == -1){
-               fprintf(stderr, "could not open %s: %s\n",
-                     desired_tape->label, strerror(errno));
-               continue;
-           }
-           fprintf(stderr, "Reading %s to fd %d\n", desired_tape->label, tapefd);
-
-           read_file_header(&file, tapefd, 1, flags);
-           label = stralloc(desired_tape->label);
-       }
-       /*
-        * Make sure we can read whatever tape is loaded, then grab the label.
-        */
-       else if(cur_tapedev && newtape){
-           if(tape_stat(cur_tapedev,&stat_tape)!=0) {
-               error("could not stat %s: %s", cur_tapedev, strerror(errno));
-           }
 
-           if((err = tape_rewind(cur_tapedev)) != NULL) {
-               fprintf(stderr, "Could not rewind device '%s': %s\n",
-                        cur_tapedev, err);
-               wrongtape = 1;
-           }
-           if((tapefd = tape_open(cur_tapedev, 0)) < 0){
-               fprintf(stderr, "could not open tape device %s: %s\n",
-                        cur_tapedev, strerror(errno));
-               wrongtape = 1;
-           }
-
-           if (!wrongtape) {
-               read_file_header(&file, tapefd, 0, flags);
-               if (file.type != F_TAPESTART) {
-                   fprintf(stderr, "Not an amanda tape\n");
-                   tapefd_close(tapefd);
-                   wrongtape = 1;
-               } else {
-                   memcpy(&tapestart, &file, sizeof(dumpfile_t));
-                   label = stralloc(file.name);
-               }
-           }
-       } else if(newtape) {
-         wrongtape = 1; /* nothing loaded */
-         bytes_read = -1;
-       }
-
-       /*
-        * Skip this tape if we did it already.  Note that this would let
-        * duplicate labels through, so long as they were in the same slot.
-        * I'm over it, are you?
-        */
-       if(label && newtape && !isafile && !wrongtape){
-           for(tape_seen = seentapes; tape_seen; tape_seen = tape_seen->next){
-               if(!strcmp(tape_seen->label, label) &&
-                       !strcmp(tape_seen->slotstr, curslot)){
-                   fprintf(stderr, "Saw repeat tape %s in slot %s\n", label, curslot);
-                   wrongtape = 1;
-                   amfree(label);
-                   break;
-               }
-           }
-       }
-
-       /*
-        * See if we've got the tape we were looking for, if we were looking
-        * for something specific.
-        */
-       if((desired_tape || !cur_tapedev) && newtape && !isafile && !wrongtape){
-           if(!label || (flags->check_labels &&
-                   desired_tape && strcmp(label, desired_tape->label) != 0)){
-               if(label){
-                   fprintf(stderr, "Label mismatch, got %s and expected %s\n", label, desired_tape->label);
-                   if(have_changer && !backwards){
-                       fprintf(stderr, "Changer can't go backwards, restoring anyway\n");
-                   }
-                   else wrongtape = 1;
-               }
-               else fprintf(stderr, "No tape device initialized yet\n");
-           }
-       }
-           
-
-       /*
-        * If we have an incorrect tape loaded, go try to find the right one
-        * (or just see what the next available one is).
-        */
-       if((wrongtape || !newtape) && !isafile){
-           if(desired_tape){
-               tapefd_close(tapefd);
-               if(have_changer){
-                   fprintf(stderr,"Looking for tape %s...\n", desired_tape->label);
-                   if(backwards){
-                       searchlabel = desired_tape->label; 
-                       changer_find(NULL, scan_init, loadlabel_slot, desired_tape->label);
-                   }
-                   else{
-                       changer_loadslot("next", &curslot, &cur_tapedev);
-                   }
-                   while(have_changer && !cur_tapedev){
-                       fprintf(stderr, "Changer did not set the tape device (slot empty or changer misconfigured?)\n");
-                       changer_loadslot("next", &curslot, &cur_tapedev);
-                   }
-               }
-               else {
-                   char *input = NULL;
-
-                    if (!flags->amidxtaped) {
-                        fprintf(prompt_out,
-                                "Insert tape labeled %s in device %s "
-                                "and press return\n", 
-                                desired_tape->label, cur_tapedev);
-                        fflush(prompt_out);
-                        input = agets(stdin);
-                        amfree(input);
-                    } else if (their_features &&
-                              am_has_feature(their_features,
-                                             fe_amrecover_FEEDME)) {
-                        fprintf(prompt_out, "FEEDME %s\n",
-                                desired_tape->label);
-                        fflush(prompt_out);
-                        input = agets(stdin); /* Strips \n but not \r */
-                        if (strcmp("OK\r", input) != 0) {
-                            error("Got bad response from amrecover: %s",
-                                  input);
-                        }
-                        amfree(input);
-                    } else {
-                        error("Client doesn't support fe_amrecover_FEEDME");
-                   }
-                }
-            }
-           else{
-                assert(!flags->amidxtaped);
-               if(have_changer){
-                   if(slot_num == 0)
-                       changer_loadslot("first", &curslot, &cur_tapedev);
-                   else
-                       changer_loadslot("next", &curslot, &cur_tapedev);
-                   if(have_changer && !cur_tapedev)
-                       error("Changer did not set the tape device, probably misconfigured");
-               }
-               else {
-                   /* XXX need a condition for ending processing? */
-                   char *input = NULL;
-                    fprintf(prompt_out,"Insert a tape to search and press enter, ^D to finish reading tapes\n");
-                   fflush(prompt_out);
-                    if((input = agets(stdin)) == NULL) break;
-                   amfree(input);
-               }
-           }
-           newtape = 1;
-           amfree(label);
-           continue;
-       }
-
-       newtape = 0;
-
-       slot_num++;
-
-       if(!isafile){
-           fprintf(stderr, "Scanning %s (slot %s)\n", label, curslot);
-           fflush(stderr);
-       }
-
-       tape_seen = alloc(sizeof(struct seentapes));
-       memset(tape_seen, '\0', sizeof(struct seentapes));
-
-       tape_seen->label = label;
-       tape_seen->slotstr = stralloc(curslot);
-       tape_seen->next = seentapes;
-       tape_seen->files = NULL;
-       seentapes = tape_seen;
-
-       /*
-        * Start slogging through the tape itself.  If our tapelist (if we
-        * have one) contains a list of files to restore, obey that instead
-        * of checking for matching headers on all files.
-        */
-       filenum = 0;
-       if(desired_tape && desired_tape->numfiles > 0) tapefile_idx = 0;
-
-       /* if we know where we're going, fastforward there */
-       if(flags->fsf && !isafile){
-           int fsf_by = 0;
-
-           /* If we have a tapelist entry, filenums will be store there */
-           if(tapefile_idx >= 0)
-               fsf_by = desired_tape->files[tapefile_idx]; 
-           /*
-            * older semantics assume we're restoring one file, with the fsf
-            * flag being the filenum on tape for said file
-            */
-           else fsf_by = flags->fsf;
-
-           if(fsf_by > 0){
-               if(tapefd_rewind(tapefd) < 0) {
-                   error("Could not rewind device %s: %s", cur_tapedev,
-                                                         strerror(errno));
-               }
-
-               if(tapefd_fsf(tapefd, fsf_by) < 0) {
-                   error("Could not fsf device %s by %d: %s", cur_tapedev, fsf_by,
-                                                          strerror(errno));
-               }
-               else {
-                       filenum = fsf_by;
-               }
-               read_file_header(&file, tapefd, isafile, flags);
-           }
-       }
-
-       while((file.type == F_TAPESTART || file.type == F_DUMPFILE ||
-             file.type == F_SPLIT_DUMPFILE) &&
-             (tapefile_idx < 0 || tapefile_idx < desired_tape->numfiles)) {
-           int found_match = 0;
-           match_list_t *me;
-           dumplist_t *tempdump = NULL;
-
-           /* store record of this dump for inventorying purposes */
-           tempdump = alloc(sizeof(dumplist_t));
-           tempdump->file = alloc(sizeof(dumpfile_t));
-           tempdump->next = NULL;
-           memcpy(tempdump->file, &file, sizeof(dumpfile_t));
-           if(tape_seen->files){
-               for(fileentry=tape_seen->files;
-                       fileentry->next;
-                       fileentry=fileentry->next);
-               fileentry->next = tempdump;
-           }
-           else tape_seen->files = tempdump;
-
-           /* see if we need to restore the thing */
-           if(isafile) found_match = 1;
-           else if(tapefile_idx >= 0){ /* do it by explicit file #s */
-               if(filenum == desired_tape->files[tapefile_idx]){
-                   found_match = 1;
-                   tapefile_idx++;
-               }
-           }
-           else{ /* search and match headers */
-               for(me = match_list; me; me = me->next) {
-                   if(disk_match(&file, me->datestamp, me->hostname,
-                               me->diskname, me->level) != 0){
-                       found_match = 1;
-                       break;
-                   }
-               }
-           }
-
-           if(found_match){
-               char *filename = make_filename(&file);
-               fprintf(stderr, "%s: %3d: restoring ", get_pname(), filenum);
-               print_header(stderr, &file);
-               bytes_read = restore(&file, filename, tapefd, isafile, flags);
-               filenum ++;
-               amfree(filename);
-           }
-
-           /* advance to the next file, fast-forwarding where reasonable */
-           if(bytes_read == 0 && !isafile) {
-               tapefd_close(tapefd);
-               if((tapefd = tape_open(cur_tapedev, 0)) < 0) {
-                   error("could not open %s: %s",
-                         cur_tapedev, strerror(errno));
-               }
-           } else if(!isafile){
-               /* cheat and jump ahead to where we're going if we can */
-               if (!found_match && flags->fsf) {
-                   drain_file(tapefd, flags);
-                   filenum ++;
-               } else if(tapefile_idx >= 0 && 
-                         tapefile_idx < desired_tape->numfiles &&
-                         flags->fsf){
-                   int fsf_by = desired_tape->files[tapefile_idx] - filenum;
-                   if(fsf_by > 0){
-                       if(tapefd_fsf(tapefd, fsf_by) < 0) {
-                           error("Could not fsf device %s by %d: %s", cur_tapedev, fsf_by,
-                                 strerror(errno));
-                       }
-                       else filenum = desired_tape->files[tapefile_idx];
-                   }
-               } else if (!found_match && flags->fsf) {
-                   /* ... or fsf by 1, whatever */
-                   if(tapefd_fsf(tapefd, 1) < 0) {
-                       error("could not fsf device %s: %s",
-                             cur_tapedev, strerror(errno));
-                   } else {
-                       filenum ++;
-                   }
-               }
-           }
-
-
-           memcpy(&prev_rst_file, &file, sizeof(dumpfile_t));
-
-             
-           if(isafile)
-                break;
-            read_file_header(&file, tapefd, isafile, flags);
-
-           /* only restore a single dump, if piping to stdout */
-           if(!headers_equal(&prev_rst_file, &file, 1) &&
-              flags->pipe_to_fd == fileno(stdout)) break;
-       } /* while we keep seeing headers */
-
-       if(!isafile){
-           if(bytes_read == 0) {
-               /* XXX is this dain-bramaged? */
-               aclose(tapefd);
-               if((tapefd = tape_open(cur_tapedev, 0)) < 0) {
-                   error("could not open %s: %s",
-                       cur_tapedev, strerror(errno));
-               }
-           } else{
-               if(tapefd_fsf(tapefd, 1) < 0) {
-                   error("could not fsf %s: %s",
-                       cur_tapedev, strerror(errno));
-               }
-           }
-       }
-        tapefd_close(tapefd);
-
-       /* spit out our accumulated list of dumps, if we're inventorying */
-       if(logstream){
-            logline = log_genstring(L_START, "taper",
-                                   "datestamp %s label %s tape %d",
-                              tapestart.datestamp, tapestart.name, slot_num);
-            fprintf(logstream, logline);
-            for(fileentry=tape_seen->files; fileentry; fileentry=fileentry->next){
-                logline = NULL;
-                switch(fileentry->file->type){
-                   case F_DUMPFILE:
-                       logline = log_genstring(L_SUCCESS, "taper",
-                                      "%s %s %s %d [faked log entry]",
-                                      fileentry->file->name,
-                                      fileentry->file->disk,
-                                      fileentry->file->datestamp,
-                                      fileentry->file->dumplevel);
-                   break;
-               case F_SPLIT_DUMPFILE:
-                   logline = log_genstring(L_CHUNK, "taper", 
-                                      "%s %s %s %d %d [faked log entry]",
-                                      fileentry->file->name,
-                                      fileentry->file->disk,
-                                      fileentry->file->datestamp,
-                                      fileentry->file->partnum,
-                                      fileentry->file->dumplevel);
-                   break;
-               default:
-                   break;
-                }
-                if(logline){
-                   fprintf(logstream, logline);
-                   amfree(logline);
-                   fflush(logstream);
-                }
-            }
-       }
-       fprintf(stderr, "%s: Search of %s complete\n",
-                       get_pname(), tape_seen->label);
-       if(desired_tape) desired_tape = desired_tape->next;
-
-       /* only restore a single dump, if piping to stdout */
-       if(!headers_equal(&prev_rst_file, &file, 1) &&
-         flags->pipe_to_fd == fileno(stdout)) break;
-    }
-
-    while(seentapes != NULL) {
-       struct seentapes *tape_seen = seentapes;
-       seentapes = seentapes->next;
-       while(tape_seen->files != NULL) {
-           dumplist_t *temp_dump = tape_seen->files;
-           tape_seen->files = temp_dump->next;
-           amfree(temp_dump->file);
-           amfree(temp_dump);
-       }
-       amfree(tape_seen->label);
-       amfree(tape_seen->slotstr);
-       amfree(tape_seen);
-       
+    if (tapelist) {
+        restore_from_tapelist(prompt_out, prompt_in, tapelist, dumpspecs,
+                              flags, their_features, cur_tapedev, use_changer,
+                              logstream);
+    } else {
+        restore_without_tapelist(prompt_out, prompt_in, dumpspecs, flags,
+                                 their_features, cur_tapedev,
+                                 (use_changer ? slots : -1),
+                                 logstream);
     }
 
     if(logstream && logstream != stderr && logstream != stdout){
@@ -1633,13 +2149,14 @@ am_feature_t *their_features;
 /*
  * Create a new, clean set of restore flags with some sane default values.
  */
-rst_flags_t *new_rst_flags()
+rst_flags_t *
+new_rst_flags(void)
 {
-    rst_flags_t *flags = alloc(sizeof(rst_flags_t));
+    rst_flags_t *flags = alloc(SIZEOF(rst_flags_t));
 
-    memset(flags, 0, sizeof(rst_flags_t));
+    memset(flags, 0, SIZEOF(rst_flags_t));
 
-    flags->fsf = 1;
+    flags->fsf = 0;
     flags->comp_type = COMPRESS_FAST_OPT;
     flags->inline_assemble = 1;
     flags->pipe_to_fd = -1;
@@ -1652,14 +2169,16 @@ rst_flags_t *new_rst_flags()
  * Make sure the set of restore options given is sane.  Print errors for
  * things that're odd, and return -1 for fatal errors.
  */
-int check_rst_flags(rst_flags_t *flags)
+int
+check_rst_flags(
+    rst_flags_t *      flags)
 {
     int ret = 0;       
     
     if(!flags) return(-1);
 
     if(flags->compress && flags->leave_comp){
-       fprintf(stderr, "Cannot specify 'compress output' and 'leave compression alone' together\n");
+       g_fprintf(stderr, _("Cannot specify 'compress output' and 'leave compression alone' together\n"));
        ret = -1;
     }
 
@@ -1667,28 +2186,28 @@ int check_rst_flags(rst_flags_t *flags)
        struct stat statinfo;
 
        if(flags->pipe_to_fd != -1){
-           fprintf(stderr, "Specifying output directory and piping output are mutually exclusive\n");
+           g_fprintf(stderr, _("Specifying output directory and piping output are mutually exclusive\n"));
            ret = -1;
        }
        if(stat(flags->restore_dir, &statinfo) < 0){
-           fprintf(stderr, "Cannot stat restore target dir '%s': %s\n",
+           g_fprintf(stderr, _("Cannot stat restore target dir '%s': %s\n"),
                      flags->restore_dir, strerror(errno));
            ret = -1;
        }
        if((statinfo.st_mode & S_IFMT) != S_IFDIR){
-           fprintf(stderr, "'%s' is not a directory\n", flags->restore_dir);
+           g_fprintf(stderr, _("'%s' is not a directory\n"), flags->restore_dir);
            ret = -1;
        }
     }
 
     if((flags->pipe_to_fd != -1 || flags->compress) &&
            (flags->delay_assemble || !flags->inline_assemble)){
-       fprintf(stderr, "Split dumps *must* be automatically reassembled when piping output or compressing/uncompressing\n");
+       g_fprintf(stderr, _("Split dumps *must* be automatically reassembled when piping output or compressing/uncompressing\n"));
        ret = -1;
     }
 
     if(flags->delay_assemble && flags->inline_assemble){
-       fprintf(stderr, "Inline split assembling and delayed assembling are mutually exclusive\n");
+       g_fprintf(stderr, _("Inline split assembling and delayed assembling are mutually exclusive\n"));
        ret = -1;
     }
 
@@ -1698,36 +2217,39 @@ int check_rst_flags(rst_flags_t *flags)
 /*
  * Clean up after a rst_flags_t
  */
-void free_rst_flags(flags)
-rst_flags_t *flags;
+void
+free_rst_flags(
+    rst_flags_t *      flags)
 {
     if(!flags) return;
 
-    if(flags->restore_dir) amfree(flags->restore_dir);
-    if(flags->alt_tapedev) amfree(flags->alt_tapedev);
-    if(flags->inventory_log) amfree(flags->inventory_log);
+    amfree(flags->restore_dir);
+    amfree(flags->alt_tapedev);
+    amfree(flags->inventory_log);
 
     amfree(flags);
 }
 
 
-/*
- * Clean up after a match_list_t
- */
-void free_match_list(match_list)
-match_list_t *match_list;
+printf_arglist_function3(
+    void send_message,
+    FILE *, prompt_out,
+    rst_flags_t *, flags,
+    am_feature_t *, their_features,
+    char *, format)
 {
-    match_list_t *me;
-    match_list_t *prev = NULL;
-  
-    for(me = match_list; me; me = me->next){
-       /* XXX freeing these is broken? can't work out why */
-/*     if(me->hostname) amfree(me->hostname);
-       if(me->diskname) amfree(me->diskname);
-       if(me->datestamp) amfree(me->datestamp);
-       if(me->level) amfree(me->level); */
-       if(prev) amfree(prev);
-       prev = me;
-    }
-    if(prev) amfree(prev);
+    va_list argp;
+    char linebuf[STR_SIZE];
+
+    arglist_start(argp, format);
+    g_vsnprintf(linebuf, SIZEOF(linebuf)-1, format, argp);
+    arglist_end(argp);
+
+    g_fprintf(stderr,"%s\n", linebuf);
+    if (flags->amidxtaped && their_features &&
+       am_has_feature(their_features, fe_amrecover_message)) {
+       g_fprintf(prompt_out, "MESSAGE %s\r\n", linebuf);
+       fflush(prompt_out);
+    }
 }
+