/*
 * Preliminary:
 *
 * i do not care about little pieces of memory, that are
 * allocated once during program execution and not used any
 * longer from some certain time on. E.g. if a string is
 * allocated via strdup:
 *
 *  char * str = strdup("some stuff");
 *
 * i'm not always freeing it the next time this pointer will
 * be assigned a new value. If wasting 100 Bytes is a problem,
 * buy a new machine. Call me, what you like.
 */

#include <conf.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/utsname.h>
#ifdef	HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include <unistd.h>
#include <stdarg.h>
#include <utils.h>
#include <fileutil.h>
#include <x_regex.h>
#include <backup.h>

#define	CLEANUP		{ goto cleanup; }

#define	ES		((UChar *) "")

#define	WS		"[ \t]*"

#define	BACKUP_RE	WS "[Bb][Aa][Cc][Kk][Uu][Pp]"
#define	CARTRIDGE_RE	WS "[Cc][Aa][Rr][Tt][Rr][Ii][Dd][Gg][Ee]"
#define	FILE_RE		WS "[Ff][Ii][Ll][Ee]"
#define	SERVER_RE	WS "[Ss][Ee][Rr][Vv][Ee][Rr]"
#define	PORT_RE		WS "[Pp][Oo][Rr][Tt]"
#define	NCARTS_RE	WS "[Nn][Uu][Mm]\\([Bb][Ee][Rr]\\)?" WS "[Oo][Ff]" \
			WS "[Cc][Aa][Rr][Tt]\\([Rr][Ii][Dd][Gg][Ee]\\)?[Ss]"

#define	EMREC_PREFIX	"~~"
#define	EMREC_PREFIX_RE	"^" WS EMREC_PREFIX

#define	BU_NO_LOCK		0
#define	BU_LOCKED		1
#define	BU_GOT_LOCK		2
#define	BU_CANT_LOCK		99

#define	MODE_FULL_BACKUP	0
#define	MODE_INCR_BACKUP	1
#define	MODE_PRINT_ERRORS	2
#define	MODE_RESTORE		3
#define	MODE_VERIFYBU		4

#define	CLEAR_LIST		0
#define	ACCESS_POSITION		1
#define	INSERT_POSITION		2

#define	FIRST_NUM_ARG		1
#define	IGNORE_NUM_ARG_TRAILERS	2

typedef	struct _scandir_args_ {
  int		fd;
}	ScanDirArgs;

typedef struct utsname	UName;

typedef struct bu_tapeentry {
  UChar		*server;
  Int32		port;
  Int32		cart;
  Int32		file;
  time_t	budate;
  Int32		mode;
  Int32		bunum;
  Int32		inscart;
  Int32		insfile;
}	BuTapeEntry;

UChar	*cmd_res[] = {
	"[Ff][Uu][Ll][Ll]",
	"[Ii][Nn][Cc][Rr]",
	"[Ee][Rr][Rr][Oo][Rr]",
	"\\([Bb][Aa][Cc][Kk][Oo][Uu][Tt]\\|[Rr][Ee][Ss][Tt][Oo][Rr][Ee]\\)",
	"[Vv][Ee][Rr][Ii][Ff][Yy]",
		};

UChar	*option2prefix[] = {	FILECONTOPTION " " FILECONTPREFIX,
				FILECONTZOPTION " " FILECONTZPREFIX,
				LOCALDEVOPTION " " LOCALDEVPREFIX,
				NULL };

UChar	*default_paramfiles[] = { DEFAULT_CLIENT_CONFIGFILES , NULL };

UChar	*bindir = NULL;
UChar	*vardir = NULL;
UChar	*libdir = NULL;
UChar   *logdir = NULL;
UChar	*paramfile = NULL;
UChar	*partfile = NULL;
UChar	*numfile = NULL;
UChar	*oldmarkfile = NULL;
UChar	*orgoldmarkfile = NULL;
UChar	*newmarkfile = NULL;
UChar	*indexfile = NULL;
UChar	*cryptfile = NULL;
UChar	*clientprogram = NULL;

UChar	*backuphostlist = NULL;
UChar	**backuphosts = NULL;
Uns32	num_backuphosts = 0;
UChar	*backupportlist = NULL;
Int32	*backupports = NULL;
Uns32	num_backupports = 0;
Int32	serveridx = 0;

UChar	*rootdir = NULL;
UChar	*dirstobackupraw = NULL;
UChar	**dirstobackup = NULL;
UChar	*filestoskipraw = NULL;
UChar	**filestoskip = NULL;
UChar	*dirstoskipraw = NULL;
UChar	**dirstoskip = NULL;
UChar	*indexfilepart = NULL;
Int32	numindexestostore = -1;
UChar	*compresscmd = NULL;
UChar	*uncompresscmd = NULL;
Int32	compressbu = 1;
UChar	*logfile = NULL;
Int32	numparts = 1;
UChar	*startinfoprog = NULL;
UChar	*initprog = NULL;
UChar	*exitprog = NULL;
UChar	reportfile[100] = "\0";
UChar	*filelist = NULL;
Int32	compresslogfiles = 1;
UChar	**filecontentstobu = NULL;
UChar	**dont_compress = NULL;
Int32	num_dont_compress = 0;
UChar	*dont_compress_str = NULL;
UChar	*exclude_filename = NULL;

UChar	no_default_backup = 0;

Int32	mode = -1;
int	part = 1;
int	num = 1;
UChar	keep_counter = 0;
UChar	keep_timestamp = 0;

FILE	*lfp = NULL;
UChar	interrupted = 0;
UChar	logf_lost = 0;
int	insfileno = 0;
int	inscartno = 0;
UChar	*cartsetlist = NULL;
Int32	*cartsets = NULL;
Uns32	num_cartsets = 0;
UChar	detach = 0;

UChar	verbose = 0;

/* for restore */
UChar	do_counting = 0;
UChar	do_listing = 0;
UChar	*restoreroot = NULL;
UChar	restore_all = 0;
UChar	restore_em = 0;
UChar	restore_emlist = 0;
UChar	restore_EM = 0;
UChar	*beforestr = NULL;
UChar	*afterstr = NULL;
time_t	time_older = 0;
time_t	time_newer = 0;
time_t	current_bu_time = 0;
time_t	prev_bu_time = 0;
UChar	have_timerange = 0;
Int32	num_restore_paths = 0;
UChar	**restore_paths = NULL;
Int32	num_prev_backup = 0;
UChar	*num_prev_backup_str = NULL;
Int32	num_prev_run = 0;
UChar	**indexfiles = NULL, **zindexfiles = NULL;
Int32	numidx, num_indexfiles;
int	curuid;
UName	systemname;
UChar	*srchost = NULL;

RE_cmp_buffer	backup_re, cartridge_re, file_re, ncarts_re,
		server_re, port_re;
ScanDirArgs	scandir_args;

UChar		*lockfile = NULL;
int		lockfd;
UChar		locked = BU_NO_LOCK;
struct flock	lockb;


#if	MAXPATHLEN < 1000
#define	TMPBUFSIZ	2000
#else
#define	TMPBUFSIZ	(MAXPATHLEN * 2)
#endif

UChar	tmpbuf[TMPBUFSIZ];

UChar	*lockdirs[] = {
		"/var/locks", "/var/lock", "/var/spool/locks",
		"/var/spool/lock", "/var/tmp", "/tmp", NULL,
};

UChar	*tmpdirs[10] = { "pwd - dummy",
		FN_DIRSEPSTR "tmp" FN_DIRSEPSTR "%N",
		FN_DIRSEPSTR "var" FN_DIRSEPSTR "tmp" FN_DIRSEPSTR "%N",
		FN_DIRSEPSTR "temp" FN_DIRSEPSTR "%N",
		NULL};
UChar	*tmpfiles[10];

ParamFileEntry	entries[] = {
	{ &dirstobackupraw, NULL,
	(UChar *) "^[ \t]*[Dd]ire?c?t?o?r?i?e?s[ \t_-]*[Tt]o[ \t_-]*[Bb]ackup:?[ \t]*",
		TypeUCharPTR	},
	{ &backuphostlist, NULL,
	(UChar *) "^[ \t]*[Bb]ackup[ \t_-]*[Hh]osts?:?[ \t]*",
		TypeUCharPTR	},
	{ &backupports, &num_backupports,
	(UChar *) "^[ \t]*[Bb]ackup[ \t_-]*[Pp]orts?:?[ \t]*",
		TypeInt32PTR	},
	{ &rootdir, NULL,
	(UChar *) "^[ \t]*[Rr]oot[ \t_-]*[Dd]ire?c?t?o?r?y?:?[ \t]*",
		TypeUCharPTR	},
	{ &filestoskipraw, NULL,
	(UChar *) "^[ \t]*[Ff]iles[ \t_-]*[Tt]o[ \t_-]*[Ss]kip:?[ \t]*",
		TypeUCharPTR	},
	{ &dirstoskipraw, NULL,
	(UChar *) "^[ \t]*[Dd]ire?c?t?o?r?i?e?s[ \t_-]*[Tt]o[ \t_-]*[Ss]kip:?[ \t]*",
		TypeUCharPTR	},
	{ &indexfilepart, NULL,
	(UChar *) "^[ \t]*[Ii]ndex[ \t_-]*[Ff]ile[ \t_-]*[Pp]art:?[ \t]*",
		TypeUCharPTR	},
	{ &numindexestostore, NULL,
	(UChar *) "^[ \t]*[Nn]um[-_ \t]*[Ii]nd\\(ic\\|ex\\)es[-_ \t]*[Tt]o[ \t_-]*[Ss]tore:?",
		TypeInt32	},
	{ &compresscmd, NULL,
	(UChar *) "^[ \t]*[Cc]ompress[-_ \t]*[Cc]o?m?ma?n?d:?[ \t]*",
		TypeUCharPTR	},
	{ &uncompresscmd, NULL,
	(UChar *) "^[ \t]*[Uu]ncompress[-_ \t]*[Cc]o?m?ma?n?d:?[ \t]*",
		TypeUCharPTR	},
	{ &dont_compress_str, NULL,
	(UChar *) "^[ \t]*[Dd]o[-_ \t]*[Nn][\'o]?t[-_ \t]*[Cc]ompress:?[ \t]*",
		TypeUCharPTR	},
	{ &exclude_filename, NULL,
	(UChar *) "^[ \t]*[Ee]xclu?d?e?[-_ \t]*[Ll]ist[-_ \t]*[Ff]ile[-_ \t]*[Nn]?a?m?e?:?[ \t]*",
		TypeUCharPTR	},
	{ &logfile, NULL,
	(UChar *) "^[ \t]*[Ll]ogg?i?n?g?[-_ \t]*[Ff]ile:?[ \t]*",
		TypeUCharPTR	},
	{ &numparts, NULL,
	(UChar *) "^[ \t]*[Nn]um[-_ \t]*[Bb]ackup[-_ \t]*[Pp]arts:?",
		TypeInt32	},
	{ &cartsets, &num_cartsets,
	(UChar *) "^[ \t]*[Cc]artr?i?d?g?e?[-_ \t]*[Ss]ets?:?",
		TypeInt32PTR	},
	{ &startinfoprog, NULL,
	(UChar *) "^[ \t]*[Ss]tartu?p?[-_ \t]*[Ii]nfo[-_ \t]*[Pp]rogram:?[ \t]*",
		TypeUCharPTR	},
	{ &initprog, NULL,
	(UChar *) "^[ \t]*[Ii]nit[-_ \t]*[Pp]rogram:?[ \t]*",
		TypeUCharPTR	},
	{ &exitprog, NULL,
	(UChar *) "^[ \t]*[Ee]xit[-_ \t]*[Pp]rogram:?[ \t]*",
		TypeUCharPTR	},
	{ &compresslogfiles, NULL,
	(UChar *) "^[ \t]*[Cc]ompress[-_ \t]*[Ll]ogg?i?n?g?[-_ \t]*[Ff]iles:?",
		TypeInt32	},
	{ &compressbu, NULL,
	(UChar *) "^[ \t]*[Cc]ompress[-_ \t]*[Bb]ackupe?d?[-_ \t]*\\([Ff]iles\\)?:?",
		TypeInt32	},
	{ &cryptfile, NULL,
	(UChar *) "^[ \t]*\\([Ee]n\\)?[Cc]rypti?o?n?[ \t_-]*[Kk]ey[ \t_-]*[Ff]ile:?[ \t]*",
		TypeUCharPTR	},
	{ &vardir, NULL,
	(UChar *) "^[ \t]*[Vv][Aa][Rr][-_ \t]*[Dd]ire?c?t?o?r?y?:?[ \t]*",
		TypeUCharPTR	},
};

static void	print_errors(int, char **);
static void	verifybu(int, char **);
static void	restore(int, char **);
static void	restoreall(int, char **);
static void	restoreem(int, char **);
static Int32	get_restore_args(int, char **);
static Int32	get_perror_args(int, char **);
static Int32	get_verify_args(int, char **);
static Int32	get_backup_args(int, char **);
static int	get_arg_num(int, char **, Int32 *, Int8);
static Int32	write_to_restore(UChar **, Int32, UChar);
static UChar	*repl_substring_safe(UChar *, UChar *, UChar *);
static void	matches_check_timerange(UChar **, int *, time_t *,
					time_t *, time_t, time_t);
static Int32	get_tapepos(Int32 *, Int32 *, Int32 *, Int8, UChar *, Int32);
static Int32	get_streamerstate(UChar *, UChar *, Int32);
static Int32	read_header_line(UChar *, Int32 *, time_t *, Int32 *, UChar *);
static int	trailingint(UChar *);
static void	compose_clntcmd(UChar *, UChar *, UChar *, Int32, Int32,
			UChar *, Int32, Int32, time_t, time_t, Int32, UChar *);

static void
do_exit(int s)
{
  int	fd;

  if(locked == BU_GOT_LOCK){
#if	0	/* unnecessary: close removes any lock */
    lockb.l_type = F_UNLCK;
    fcntl(lockfd, F_SETLK, &lockb);
#endif

    close(lockfd);

    unlink(lockfile);
  }

  if(exitprog){
    sprintf(tmpbuf, "%d", s);

    exitprog = repl_substring_safe(exitprog, "%r", reportfile);
    exitprog = repl_substring_safe(exitprog, "%e", tmpbuf);
    exitprog = repl_substring_safe(exitprog, "%l", filelist ? filelist :
		(UChar *) "<filename-logfile not yet determined");

    if((fd = open(reportfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) >= 0)
	close(fd);

    system(exitprog);

    if(reportfile[0])
	unlink(reportfile);
  }

  exit(s);
}

static void
errmsg(UChar * msg, ...)
{
  va_list	args;

  va_start(args, msg);

  if(lfp){
    fprintf(lfp, "%s, ", actimestr());
    vfprintf(lfp, msg, args);
    fprintf(lfp, ".\n");
    fflush(lfp);
  }

  vfprintf(stderr, msg, args);
  fprintf(stderr, ".\n");

  va_end(args);
}

static void
logmsg(UChar * msg, ...)
{
  va_list	args;

  va_start(args, msg);

  if(lfp){
    fprintf(lfp, "%s, ", actimestr());
    vfprintf(lfp, msg, args);
    fprintf(lfp, ".\n");
    fflush(lfp);
  }

  va_end(args);
}

static void
nomemerrexit()
{
  fprintf(stderr, "Error: No memory.\n");
  do_exit(3);
}

UChar *
repl_substring_safe(UChar * org, UChar * to_subst, UChar * subst_by)
{
  UChar		*newstr;

  newstr = repl_substring(org, to_subst, subst_by);
  if(!newstr)
    nomemerrexit();

  return(newstr);
}

static void
usage(UChar * pname)
{
  pname = FN_BASENAME(pname);

  switch(mode){
   case MODE_RESTORE:
    fprintf(stderr,
        "Usage: %s [ -nl ] [ -<past-backup-no> ] [ -C <root-directory> ] \\\n"
        "               [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
	"               [ -c <configuration-file> ] \\\n"
        "               [ -A \"<after-date>\" ] [ -B \"<before-date>\" ] \\\n"
	"               [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
	"               [ -k <encryption-key-file> ] \\\n"
	"               [ -z <compress-cmd> <uncompress-cmd> ] \\\n"
        "               [ -p ] <path-pattern> [ [ -p ] <path-patterns> [ ... ] ]\n"
        "       %s -a [ -<past-backup-no> ] [ -C <root-directory> ] \\\n"
        "               [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
	"               [ -c <configuration-file> ] \\\n"
	"               [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
	"               [ -k <encryption-key-file> ] \\\n"
	"               [ -z <compress-cmd> <uncompress-cmd> ]\n"
	"       %s -{ef} [ -C <root-directory> ] [ -h <backuphosts> ] \\\n"
       	"               [ -P <backup-ports> ] [ -V <var-directory> ] \\\n"
	"               [ -z <compress-cmd> <uncompress-cmd> ] \\\n"
	"               [ -k <encryption-key-file> ] \\\n"
	"               [ -c <configuration-file> ] < <startup-info-file>\n"
	"       %s -E [ -nl ] [ -C <root-directory> ] [ -h <backuphosts> ] \\\n"
       	"               [ -P <backup-ports> ] [ -V <var-directory> ] \\\n"
	"               [ -z <compress-cmd> <uncompress-cmd> ] \\\n"
	"               [ -k <encryption-key-file> ] \\\n"
	"               [ -c <configuration-file> ] [ -H <orig-host> ] \\\n"
	"               [ <cartridge-number> | <cartridge-range> ] ... ]\n",
                        pname, pname, pname, pname);
    break;

   case MODE_PRINT_ERRORS:
    fprintf(stderr,
	"Usage: %s [ -c <configuration-file> ] [ -<past-backup-no> ] \\\n"
		"                    [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
		"                    [ -N <num-indexes-to-store> ] \\\n"
		"                    [ -z <compress-cmd> <uncompress-cmd> ]\n",
		pname);
    break;

   case MODE_VERIFYBU:
    fprintf(stderr,
	"Usage: %s [ -c <configuration-file> ] \\\n"
		"              [ -<past-run-no>[.<past-backup-no>] ] \\\n"
		"              [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
		"              [ -C <root-directory> ] [ -S <cartridge-set> ] \\\n"
		"              [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
		"              [ -k <encryption-key-file> ] \\\n"
		"              [ -z <compress-cmd> <uncompress-cmd> ]\n"
		, pname);
    break;

   default:
    fprintf(stderr, "Usage: %s [ -da ] [ {+-}LB ] [ <files> <directories> ... ] \\\n"
		"                   [ -C <root-directory> ] [ -F \"<files-to-skip>\" ] \\\n"
		"                   [ -D \"<directories-to-skip>\" ] \\\n"
		"                   [ -c <configuration-file> ] \\\n"
		"                   [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
		"                   [ -I <indexfile-part> ] \\\n"
		"                   [ -N <num-indexes-to-store> ] \\\n"
		"                   [ -z <compress-cmd> <uncompress-cmd> ] \\\n"
		"                   [ -s \"<dont-compress-patterns>\" ] \\\n"
		"                   [ -X <exclude-list-file> ] [ -l <logfile> ] \\\n"
		"                   [ -i <startup-info-program> ] \\\n"
		"                   [ -b <init-program> ] [ -e <exit-program> ] \\\n"
		"                   [ -k <encryption-key-file> ] \\\n"
		"                   [ -V <var-directory> ] [ -S <cartridge-sets> ]\n", pname);
  }

  do_exit(1);
}

static void
sig_handler(int s)
{
  if(mode != MODE_INCR_BACKUP && mode != MODE_FULL_BACKUP){
    kill(getpid(), s);
    do_exit(2);
  }

  signal(s, sig_handler);

  if(s != SIGPIPE){
    if(interrupted)
	return;

    fprintf(stderr, "Interrupted. Cleanup in progress, please stand by.\n");
  }
  else{
    if(!interrupted)
	errmsg("Connection to client process lost, exiting");

    logf_lost = 1;
  }

  interrupted = 1;
}

static void
exitcleanup()
{
  if(!keep_timestamp){
    switch(mode){
     case MODE_FULL_BACKUP:
      if(!access(orgoldmarkfile, R_OK)){
	unlink(oldmarkfile);
	rename(orgoldmarkfile, oldmarkfile);
      }
      break;

     case MODE_INCR_BACKUP:
      unlink(newmarkfile);

     default:
      break;
    }
  }
}

static void
intrpt_exit()
{
  exitcleanup();

  if(lfp){
    switch(mode){
     case MODE_FULL_BACKUP:
      if(numparts > 1)
	logmsg("Full backup part %d interrupted", part);
      else
	logmsg("Full backup interrupted");
      break;

     case MODE_INCR_BACKUP:
	logmsg("Incremental backup interrupted");
      break;

     default:
      break;
    }

    fclose(lfp);
  }

  do_exit(2);
}

static void
failed_exit(Int32 s)
{
  exitcleanup();

  if(lfp){
    if(mode == MODE_FULL_BACKUP){
      if(numparts > 1)
	logmsg("Full backup part %d failed", part);
      else
	logmsg("Full backup failed");
    }
    if(mode == MODE_INCR_BACKUP){
      logmsg("Incremental backup failed");
    }
    fclose(lfp);
  }

  do_exit(s);
}

Int32
set_lock()
{
  struct stat	statb;
  char		buf[20];
  Int32		i;

  if(locked == BU_GOT_LOCK)
    return((Int32) locked);

  if(!lockfile)
    return((Int32) BU_CANT_LOCK);

  i = lstat(lockfile, &statb);
  if(!i && !IS_REGFILE(statb)){
    if(unlink(lockfile)){
	errmsg("Error: Cannot remove lock file entry \"%s\", that is not a file",
		lockfile);
	return( (Int32) (locked = BU_CANT_LOCK) );
    }

    i = 1;
  }
  if(!i){
    lockfd = open(lockfile, O_WRONLY | O_SYNC);
    if(lockfd < 0){
      errmsg("Warning: Lock file \"%s\" exists, but can't open it",
		lockfile);
      return( (Int32) (locked = BU_CANT_LOCK) );
    }
  }
  else{
    lockfd = open(lockfile, O_WRONLY | O_CREAT | O_SYNC, 0644);
    if(lockfd < 0){
	errmsg("Error: Cannot create lock file \"%s\"", lockfile);
	return( (Int32) (locked = BU_CANT_LOCK) );
    }
  }

  lockb.l_type = F_WRLCK;
  if(fcntl(lockfd, F_SETLK, &lockb)){
    return( (Int32) (locked = BU_LOCKED) );
  }

  sprintf(buf, "%d\n", (int) getpid());
  write(lockfd, buf, strlen(buf) + 1);

  return( (Int32) (locked = BU_GOT_LOCK) );
}

static Int32
read_uns_file(UChar * filename, int * num)
{
  FILE	*fp;

  *num = 0;
  fp = fopen(filename, "r");
  if(fp){
    if(fscanf(fp, "%d", num) <= 0)
	*num = 0;

    fclose(fp);
  }

  return(*num <= 0);
}

static Int32
write_uns_file(UChar * filename, int num)
{
  FILE		*fp;
  Int32	ret = 0;

  fp = fopen(filename, "w");
  if(fp){
    if(fprintf(fp, "%d\n", num) <= 0)
	ret = 1;

    fclose(fp);
  }
  else
    ret = 1;

  return(ret);
}

static Int32
write_filename(UChar * name, void * data)
{
  int		fd;
  UChar		*cptr = NULL;
  Int32	ret = 0, needed_size;
  UChar		allocated = 0;
  ScanDirArgs	*scandirargs;

  scandirargs = (ScanDirArgs *) data;

  fd = scandirargs->fd;

  if(strchr(name, '\\')){
    name = repl_substring_safe(name, "\\", "\\\\");
  }
  if(strchr(name, '"')){
    name = repl_substring_safe(name, "\"", "\\\"");
  }
  needed_size = strlen(name) + 5;
  if(needed_size > TMPBUFSIZ){
    cptr = NEWP(UChar, needed_size);
    if(!cptr){
      ret = errno;
      CLEANUP;
    }

    allocated = 1;
  }
  else{
    cptr = tmpbuf;
  }

  strcpy(cptr + 1, name);
  cptr[0] = '\"';
  strcat(cptr, "\"\n");
  name = cptr;

  write(fd, name, strlen(name));

 cleanup:
  if(cptr && allocated)
    free(cptr);

  if(interrupted)
    return(1000);

  return(ret);
}

static Int32
err_write_filename(UChar * name, void * data)
{
  int		fd;
  ScanDirArgs	*scandirargs;

  scandirargs = (ScanDirArgs *) data;

  fd = scandirargs->fd;

  scandirargs->fd = 1;

  /* don't: write_filename(name, data));*/

  scandirargs->fd = fd;

  return(0);
}

#define	INDEX_FILEENTRY(n)	((n) >= 0)
#define	INDEX_EMPTY(n)		((n) == -1)
#define	INDEX_COMMENT(n)	((n) == -2)
#define	INDEX_NOCONTENTS(n)	(INDEX_EMPTY(n) || INDEX_COMMENT(n))
#define	INDEX_RECOGNIZED(n)	((n) > -10)
#define	INDEX_UNRECOGNIZED(n)	((n) <= -10)

/* returns:
   -1		empty
   -2		comment
   <= -10:	not recognized
   >=0		fileposition
*/

static Int32
eval_index_line(
  UChar		*line,
  UChar		**server,
  Uns32		*port,
  Int32		*cart,
  Int32		*fileno,
  int		*uid)
{
  UChar		*cptr, *orgline, *newmem, *colon = NULL, a;
  int		p = 0, c = 0, f = 0, u = 0, servlen = 0, i;

  if(empty_string(line))
    return(-1);

  if(*(first_nospace(line)) == '#')
    return(-2);

  orgline = line;

  if( (colon = strchr(line, ':')) )
    *colon = '\0';

  cptr = strstr(line, PORTSEP);

  if(colon)
    *colon = ':';

  if(cptr){
    servlen = cptr - line;

    a = *cptr;
    *cptr = '\0';
    i = isfqhn(line);
    *cptr = a;

    if(!i || sscanf(cptr + 1, "%d" LOCSEP, &p) <= 0)
	return(-10);

    line = strstr(line, LOCSEP) + 1;
  }

  if(sscanf(line, "%d.%d:", &c, &f) < 2 
		&& sscanf(line, "%d.%d" UIDSEP "%d:", &c, &f, &u) < 3){
    return(-11);
  }

  if(servlen && server){
    do{
	newmem = NEWP(UChar, servlen + 1);
	if(!newmem)
	  ms_sleep(200);
    }while(!newmem);

    strncpy(newmem, orgline, servlen);
    newmem[servlen] = '\0';
    *server = newmem;
  }
  if(p && port)
    *port = p;
  if(u && uid)
    *uid = u;
  if(c && cart)
    *cart = c;
  if(f && fileno)
    *fileno = f;

  return(first_nospace(strstr(line, ":") + 1) - orgline);
}

main(int argc, char ** argv)
{
  UChar		*backuphome, *cptr, **cpptr, **cpptr2, **cpptr3;
  UChar		*zippedidxf, *tmpidxf, *tmperrf, c;
  UChar		*dirsarr[2], must_read_fnamlog = 0;
  UChar		*filename, *uncompcmd;
  Int32		i, j, n, lck, num_inlines = 0, num_outlines = 0;
  int		startnum, startcart, startfile, pos, p;
  FILE		*fp;
  int		pid, pst, pp[3], lfd, *errflp;
  int		ifd, opid, ipid, errfd;
  FindParams	findparams;
  UChar		success = 1, *infile, *outfile, inunzip, outzip;
  UChar		*rename_from = NULL, *rename_to = NULL;
  struct stat	statb, statb2;

  curuid = getuid();

  errflp = pp + 2;

  cptr = FN_BASENAME((UChar *) argv[0]);
  for(i = 0; i < sizeof(cmd_res) / sizeof(*cmd_res); i++)
    if(re_find_match_once(cmd_res[i], cptr, NULL, NULL) >= 0){
	mode = i;
	break;
    }
  if(mode < 0){
    fprintf(stderr, "Error: Command name %s not recognized.\n", cptr);
    do_exit(1);
  }

  /* check usage */
  switch(mode){
   case MODE_RESTORE:
    get_restore_args(argc, argv);
    break;

   case MODE_PRINT_ERRORS:
    get_perror_args(argc, argv);
    break;

   case MODE_VERIFYBU:
    get_verify_args(argc, argv);
    break;

   default:
    get_backup_args(argc, argv);
  }

  i = uname(&systemname);
  if(i < 0){
    fprintf(stderr, "Warning: Cannot determine hostname.\n");
    strcpy(systemname.nodename, "???");
  }
  cptr = strchr(systemname.nodename, '.');
  if(cptr)
    *cptr = '\0';

  if(detach){
    if(fork()){
	exit(0);
    }
    else{
	detach_from_tty();

	ms_sleep(1000 * 30);
    }
  }

  /* set signal handlers */
  signal(SIGTERM, sig_handler);
  signal(SIGHUP, sig_handler);
  signal(SIGINT, sig_handler);
  signal(SIGSEGV, sig_handler);
  signal(SIGBUS, sig_handler);
  signal(SIGPIPE, sig_handler);

  /* compile some regular expressions */
  SETZERO(backup_re);
  SETZERO(cartridge_re);
  SETZERO(file_re);
  SETZERO(server_re);
  SETZERO(port_re);
  SETZERO(ncarts_re);
  if(re_compile_pattern(CARTRIDGE_RE, strlen(CARTRIDGE_RE), &cartridge_re)
	|| re_compile_pattern(FILE_RE, strlen(FILE_RE), &file_re)
	|| re_compile_pattern(SERVER_RE, strlen(SERVER_RE), &server_re)
	|| re_compile_pattern(PORT_RE, strlen(PORT_RE), &port_re)
	|| re_compile_pattern(NCARTS_RE, strlen(NCARTS_RE), &ncarts_re)
	|| re_compile_pattern(BACKUP_RE, strlen(BACKUP_RE), &backup_re))
    nomemerrexit();

  /* determine home directory */
  backuphome = getenv("BACKUP_HOME");

#ifdef	ORIG_DEFAULTS

  if(!backuphome){
    backuphome = find_program(argv[0]);
    if(!backuphome){
	errmsg("Error: Cannot find program file of \"%s\"", argv[0]);
	do_exit(4);
    }
    cptr = mkabspath(backuphome, NULL);
    if(!cptr)
	nomemerrexit();
    free(backuphome);
    backuphome = resolvepath__(cptr, NULL);
    if(!backuphome){
	errmsg("Error: Cannot follow path \"%s\"", cptr);
	do_exit(29);
    }
    free(cptr);

    cptr = FN_LASTDIRDELIM(backuphome);
    if(!cptr){
      errmsg("Strange error: cannot separate binary directory");
      do_exit(5);
    }
    *cptr = '\0';
    cptr = FN_LASTDIRDELIM(backuphome);
    if(!cptr){
      errmsg("Strange error: cannot separate client directory");
      do_exit(5);
    }
    *cptr = '\0';
  }
  else
    backuphome = strdup(backuphome);

  if(!backuphome)
    nomemerrexit();

  /* construct file- and dirnames */
  bindir = strapp(backuphome, FN_DIRSEPSTR "bin");
  vardir = strapp(backuphome, FN_DIRSEPSTR "var");
  libdir = strapp(backuphome, FN_DIRSEPSTR "lib");
  logdir = strdup(vardir);

  if(!bindir || !vardir || !libdir || !logdir)
    nomemerrexit();

  if(!paramfile)
    paramfile = strapp(libdir, FN_DIRSEPSTR "backup.conf");

#else	/* defined(ORIG_DEFAULTS) */

  if(!backuphome){
     /* construct file- and dirnames */
     bindir = DEFBINDIR;
     vardir = DEFVARDIR;
     libdir = DEFLIBDIR;
     logdir = DEFLOGDIR;
  }
  else {
     backuphome = strdup(backuphome);
     /* construct file- and dirnames */
     bindir = strapp(backuphome, FN_DIRSEPSTR "bin");
     vardir = strapp(backuphome, FN_DIRSEPSTR "var");
     libdir = strapp(backuphome, FN_DIRSEPSTR "lib");
     logdir = strapp(backuphome, FN_DIRSEPSTR "var");
  }

  if(!bindir || !vardir || !libdir || !logdir)
     nomemerrexit();

  if(!paramfile)
    paramfile = strapp(libdir, FN_DIRSEPSTR DEFCLIENTCONF);

#endif	/* if else defined(ORIG_DEFAULTS) */

  if(!stat(paramfile, &statb) && access(paramfile, R_OK)){
    errmsg("Error: Cannot read parameter file \"%s\"", paramfile);
    do_exit(6);
  }
  if(stat(paramfile, &statb)){
    for(cpptr = default_paramfiles; *cpptr; cpptr++){
      paramfile = *cpptr;
      if(!stat(paramfile, &statb) && access(paramfile, R_OK)){
	errmsg("Error: Cannot read parameter file \"%s\"", paramfile);
	do_exit(6);
      }
      if(!stat(paramfile, &statb))
	break;
    }

    if(!*cpptr){
	errmsg("Warning: No configuration file found, using defaults");
	/*do_exit(6);*/
    }
  }

  /* read parameter file */
  i = read_param_file(paramfile, entries,
		sizeof(entries) / sizeof(entries[0]), NULL, NULL);
  if(i){
    errmsg("Error: Cannot read parameter file");
    do_exit(6);
  }

  /* override by commandline-arguments */
  switch(mode){
   case MODE_RESTORE:
    get_restore_args(argc, argv);
    break;

   case MODE_PRINT_ERRORS:
    get_perror_args(argc, argv);
    break;

   case MODE_VERIFYBU:
    get_verify_args(argc, argv);
    break;

   default:
    get_backup_args(argc, argv);
  }

  massage_string(vardir);
  partfile = strapp(vardir, FN_DIRSEPSTR "part");
  numfile = strapp(vardir, FN_DIRSEPSTR "num");
  oldmarkfile = strapp(vardir, FN_DIRSEPSTR "oldmark");
  newmarkfile = strapp(vardir, FN_DIRSEPSTR "newmark");
  orgoldmarkfile = strapp(vardir, FN_DIRSEPSTR "oldmark.org");
  clientprogram = strapp(bindir, FN_DIRSEPSTR "afbackup");
  if(!dont_compress_str)
    dont_compress_str = "";
  num_dont_compress = str2wordsq(&dont_compress, dont_compress_str);
  if(!paramfile || !partfile || !oldmarkfile || !orgoldmarkfile
		|| !clientprogram || !newmarkfile || num_dont_compress < 0)
    nomemerrexit();

  if(!cryptfile)
    cryptfile = strapp(libdir, FN_DIRSEPSTR "cryptkey");
  massage_string(cryptfile);

  /* read encryption key */
  if(!stat(cryptfile, &statb)){
    if(statb.st_mode & 0044){
	fprintf(stderr,
		"Error: Encryption key file \"%s\" is readable by unprivileged users.\n",
		cryptfile);
	do_exit(11);
    }

    if(set_cryptkey(cryptfile)){
	fprintf(stderr,
		"Warning: Cannot read enough characters from encryption key file \"%s\".\n",
		cryptfile);
	fprintf(stderr,
		"	  Ignoring file, using compiled-in key.\n");
	ZFREE(cryptfile);
    }
  }
  else
    ZFREE(cryptfile);

  /* correct parameter values, if unreasonable or set defaults */
  if(!dirstobackupraw)
    dirstobackupraw = strdup("*");
  if(backuphostlist && empty_string(backuphostlist))
    ZFREE(backuphostlist);
  if(!backuphostlist)			/* set default server name */
    backuphostlist = strdup(DEFAULT_SERVER);
  while( (cptr = strchr(backuphostlist, ',')) )	/* [,;] -> SPC */
    *cptr = ' ';
  while( (cptr = strchr(backuphostlist, ';')) )
    *cptr = ' ';
  num_backuphosts = str2words(&backuphosts, backuphostlist);
  if(num_backuphosts < 0)			/* make server list */
    nomemerrexit();
  backupports = ZRENEWP(backupports, Int32, num_backuphosts * sizeof(Uns32));
  if(num_backupports > num_backuphosts)	/* ports: no more than hosts */
    num_backupports = num_backuphosts;
  i = 0;
  if( (cptr = backupportlist) ){		/* evaluate list */
    while(sscanf(cptr, "%d%n", &p, &pos) > 0 && i < num_backuphosts){
	backupports[i++] = p;
	cptr += pos;
	while(!isdigit(*cptr) && *cptr)		/* skip to next number */
	  cptr++;
    }
  }
  if(i > num_backupports)		/* max of given is new # ports */
    num_backupports = i;
  if(num_backupports < num_backuphosts){	/* set default values */
    while(num_backupports < num_backuphosts)
	backupports[num_backupports++] = -1;
  }
  cartsets = ZRENEWP(cartsets, Int32, num_backuphosts * sizeof(Int32));
  if(num_cartsets > num_backuphosts)	/* sets: no more than hosts */
    num_cartsets = num_backuphosts;
  i = 0;
  if( (cptr = cartsetlist) ){			/* evaluate list */
    while(sscanf(cptr, "%d%n", &p, &pos) > 0 && i < num_backuphosts){
	cartsets[i++] = p;
	cptr += pos;
	while(!isdigit(*cptr) && *cptr)		/* skip to next number */
	  cptr++;
    }
  }
  if(i > num_cartsets)			/* max of given is new # sets */
    num_cartsets = i;
  if(num_cartsets < num_backuphosts){
    while(num_cartsets < num_backuphosts)
	cartsets[num_cartsets++] = 1;		/* set default values */
  }
  if(!rootdir)
    rootdir = strdup(FN_DIRSEPSTR);
  if(!indexfilepart)
    indexfilepart = strapp(logdir, FN_DIRSEPSTR "backup_log.");
  if(numparts <= 0)
    numparts = 1;
  if(numindexestostore <= 0)
    numindexestostore = 1;
  if(!compresscmd || !uncompresscmd){
    uncompresscmd = compresscmd = "";
    compresslogfiles = compressbu = 0;
  }
  if(!compresscmd[0] || !uncompresscmd[0]){
    uncompresscmd = compresscmd = "";
    compresslogfiles = compressbu = 0;
  }
  if(!logfile)
    logfile = "";
  massage_string(logfile);
  if(!logfile[0])
    logfile = strapp(logdir, FN_DIRSEPSTR "backup.log");
  if(!logfile)
    nomemerrexit();
  if(startinfoprog){
    if(empty_string(startinfoprog)){
	startinfoprog = NULL;
    }
  }
  if(initprog){
    if(empty_string(initprog) ||
		(mode != MODE_INCR_BACKUP
			&& mode != MODE_FULL_BACKUP)){
	ZFREE(initprog);
    }
  }
  if(exitprog){
    if(empty_string(exitprog) ||
		(mode != MODE_INCR_BACKUP
			&& mode != MODE_FULL_BACKUP)){
	ZFREE(exitprog);
    }
    else{
	if(strstr(exitprog, "%r"))
	  tmpnam(reportfile);
    }
  }
  if(exclude_filename){
    if(empty_string(exclude_filename)){
	exclude_filename = NULL;
    }
    else
	massage_string(exclude_filename);
  }
  massage_string(rootdir);
  massage_string(indexfilepart);

  if(!lockfile){
    for(cpptr = lockdirs; *cpptr; cpptr++){
      i = stat(*cpptr, &statb);
      cptr = strchain(*cpptr, FN_DIRSEPSTR, DEFAULT_CLIENTLOCKFILE, NULL);
      if(!i){
	if(! access(*cpptr, W_OK) || ! stat(cptr, &statb2)){
	  lockfile = cptr;
	  break;
	}
	else{
	  i = setgid(statb.st_gid);

	  if(! access(*cpptr, W_OK)){
	    lockfile = cptr;
	    break;
	  }
	}
      }
    }
  }
  massage_string(lockfile);

  lck = set_lock();
  if(lck != BU_GOT_LOCK){
    j = 0;
    pid = -1;
    i = read_uns_file(lockfile, &pid);
    if(!i){
	j = ! kill(pid, 0);
    }
    else{
	j = 1;
    }

    if(j){
	errmsg("Error: An application seems to hold a lock on this functionality");
	if(pid >= 0){
	  sprintf(tmpbuf, "%d", pid);
	  errmsg("	  The process ID is %s", tmpbuf);
	}
	else{
	  errmsg("	  Cannot determine process-ID");
	}

	fprintf(stderr, "Please check if this process is an obstacle to continue.\n");
	fprintf(stderr, "Do you want to continue anyway ? (y/N) ");
	tmpbuf[0] = '\0';
	fgets(tmpbuf, 10, stdin);
	cptr = tmpbuf;
	while(isspace(*cptr) && *cptr)
	  cptr++;
	if(*cptr != 'y' && *cptr != 'Y')
	  exit(99);
    }

    if(unlink(lockfile)){
	errmsg("Warning: Cannot remove lockfile \"%s\"", lockfile);
    }
    lck = set_lock();
  }

  if(lck != BU_GOT_LOCK){
	fprintf(stderr, "Warning: Cannot set lock. Continue anyway ? (y/N) ");
	tmpbuf[0] = '\0';
	fgets(tmpbuf, 10, stdin);
	cptr = tmpbuf;
	while(isspace(*cptr) && *cptr)
	  cptr++;
	if(*cptr != 'y' && *cptr != 'Y')
	  exit(99);
  }

  if(mode == MODE_INCR_BACKUP || mode == MODE_PRINT_ERRORS
		|| mode == MODE_RESTORE || mode == MODE_VERIFYBU){
    if( (i = read_uns_file(numfile, &num)) )
	cptr = numfile;

    if(mode == MODE_PRINT_ERRORS || mode == MODE_RESTORE
		|| mode == MODE_VERIFYBU){
      if(i){
	errmsg("Error: Cannot read file \"%s\"", numfile);
	do_exit(9);
      }
    }
    else{		/* MODE_INCR_BACKUP */
      if(access(oldmarkfile, R_OK)){
	i = 1;
	cptr = oldmarkfile;
      }

      if(i){
	errmsg("Warning: Cannot read file \"%s\", switching to full backup",
				cptr);
	mode = MODE_FULL_BACKUP;
      }
    }
  }

  if(mode == MODE_PRINT_ERRORS)
    print_errors(argc, argv);

  if(mode == MODE_RESTORE)
    restore(argc, argv);

  /* change to the root directory */
  if(chdir(rootdir)){
    errmsg("Error: Cannot change to directory \"%s\"", rootdir);
    do_exit(7);
  }

  if(mode == MODE_VERIFYBU)
    verifybu(argc, argv);

  part = 1;
  /* if backup is split in pieces, get again the directories to backup */
  if(numparts > 1){
    switch(mode){
     case MODE_FULL_BACKUP:
     {
      ParamFileEntry	dirsparam;
      UChar		*partdirstobackupraw = NULL;

      read_uns_file(partfile, &part);

      part++;
      if(part > numparts)
	part = 1;
      sprintf(tmpbuf, "%d:", part);

      dirsparam.pattern = repl_substring_safe(entries[0].pattern, ":", tmpbuf);
      dirsparam.entry_ptr = &partdirstobackupraw;
      dirsparam.num_entries = NULL;
      dirsparam.type = TypeUCharPTR;

      i = read_param_file(paramfile, &dirsparam, 1, NULL, NULL);
      if(i){
	errmsg("Error: Cannot read parameter file");
	do_exit(6);
      }
      free(dirsparam.pattern);

      if(partdirstobackupraw){
	if(dirstobackupraw)
	  free(dirstobackupraw);

	dirstobackupraw = partdirstobackupraw;
      }

      break;
     }

     case MODE_INCR_BACKUP:
     {
      ParamFileEntry	*dirsparams;
      UChar		**partdirstobackupraw = NULL, *cptr;

      dirsparams = NEWP(ParamFileEntry, numparts);
      partdirstobackupraw = NEWP(UChar *, numparts);
      if(!dirsparams || !partdirstobackupraw)
	nomemerrexit();

      for(i = 0; i < numparts; i++){
	sprintf(tmpbuf, "%d:", (int) (i + 1));

	dirsparams[i].pattern =
			repl_substring_safe(entries[0].pattern, ":", tmpbuf);
	dirsparams[i].entry_ptr = partdirstobackupraw + i;
	dirsparams[i].num_entries = NULL;
	dirsparams[i].type = TypeUCharPTR;
      }

      i = read_param_file(paramfile, dirsparams, numparts, NULL, NULL);
      if(i){
	errmsg("Error: Cannot read parameter file");
	do_exit(6);
      }

      if(dirstobackupraw)
	free(dirstobackupraw);
      dirstobackupraw = strdup("");

      for(i = 0; i < numparts; i++){
	cptr = strchain(dirstobackupraw, " ",
					partdirstobackupraw[i], NULL);
	free(dirstobackupraw);
	dirstobackupraw = cptr;
	free(partdirstobackupraw[i]);
	free(dirsparams[i].pattern);
      }
      free(partdirstobackupraw);
      free(dirsparams);

      break;
     }
     default:
      break;
    }
  }

  /* convert the strings (containing lists) to string arrays */
  if(str2wordsq(&dirstobackup, dirstobackupraw ? dirstobackupraw : ES) < 0
	|| str2wordsq(&dirstoskip, dirstoskipraw ? dirstoskipraw : ES) < 0
	|| str2wordsq(&filestoskip, filestoskipraw ? filestoskipraw : ES) < 0)
    nomemerrexit();

  for(cpptr = dirstobackup; *cpptr; cpptr++){
    if(!strncmp(*cpptr, FILECONTPREFIX, i = strlen(FILECONTPREFIX))){
	cleanpath(*cpptr + i);
    }
    else if(!strncmp(*cpptr, FILECONTZPREFIX, i = strlen(FILECONTZPREFIX))){
	cleanpath(*cpptr + i);
    }
    else{
	cleanpath(*cpptr);
    }
  }
  for(cpptr = dirstoskip; *cpptr; cpptr++)
    cleanpath(*cpptr);
  for(cpptr = filestoskip; *cpptr; cpptr++)
    cleanpath(*cpptr);

#if 0	/* don't do this: it's dangerous, when paths are symlinked or cross-filesystem */
  /* resolve glob patterns */
  alldirs = NEWP(UChar *, 1);
  *alldirs = NULL;
  num_alldirs = 0;
  for(cpptr = dirstobackup; *cpptr; cpptr++){
    cpptr2 = fnglob(*cpptr);
    if(cpptr2){
      for(n = 0, cpptr3 = cpptr2; *cpptr3; cpptr3++, n++);

      alldirs = RENEWP(alldirs, UChar *, n + num_alldirs + 1);
      memcpy(alldirs + num_alldirs, cpptr2, sizeof(UChar *) * n);
      alldirs[n + num_alldirs] = NULL;
      num_alldirs += n;

      free(cpptr2);
    }

    free(*cpptr);
  }
  free(dirstobackup);
  dirstobackup = alldirs;
      
  n = num_alldirs;

  {	/* throw out redundant paths (e.g. /home/alb when /home is present) */
    for(i = 0; i < n - 1; i++){
      for(j = i + 1; j < n; j++){
	if(strlen(dirstobackup[i]) < strlen(dirstobackup[j])
		&& !strncmp(dirstobackup[i], dirstobackup[j],
						strlen(dirstobackup[i]))){
	  for(cpptr = dirstobackup + j; *cpptr; cpptr++){
	    *cpptr = *(cpptr + 1);
	  }
	  j--;
	  n--;
	}
	else if(strlen(dirstobackup[j]) < strlen(dirstobackup[i])
		&& !strncmp(dirstobackup[j], dirstobackup[i],
						strlen(dirstobackup[j]))){
	  for(cpptr = dirstobackup + i; *cpptr; cpptr++){
	    *cpptr = *(cpptr + 1);
	  }
	  i--;
	  n--;
	  break;
	}
      }
    }
  }
#endif

  for(cpptr = dirstobackup; *cpptr; cpptr++){
    for(cpptr2 = option2prefix; *cpptr2; cpptr2++){	/* substitute options */
      cptr = sscanword(*cpptr2, tmpbuf);		/* with prefix */
      if(!strcmp(tmpbuf, *cpptr)){
	sscanword(cptr, tmpbuf);
	free(*cpptr);

	if(*(cpptr + 1)){
	  *cpptr = strapp(tmpbuf, *(cpptr + 1));

	  for(cpptr3 = cpptr + 1; *cpptr3; cpptr3++)
	    *(cpptr3) = *(cpptr3 + 1);
	  cpptr--;
	}
	else
	  *cpptr = NULL;

	break;
      }
    }
  }

  for(cpptr = dirstobackup, n = 0; *cpptr; cpptr++, n++);

  for(i = 0; i < n - 1; i++){		/* remove duplicate entries */
    for(j = i + 1; j < n; j++){
      if(! strcmp(dirstobackup[j], dirstobackup[i])){
	for(cpptr2 = dirstobackup + j; *cpptr2; cpptr2++)
	  *(cpptr2) = *(cpptr2 + 1);
	j--;
	n--;
      }
    }
  }

  filecontentstobu = NEWP(UChar *, 1);
  if(! filecontentstobu)
    nomemerrexit();
  *filecontentstobu = NULL;
  i = 0;
  for(cpptr = dirstobackup; *cpptr; cpptr++){
    if(!strncmp(*cpptr, FILECONTPREFIX, strlen(FILECONTPREFIX))
		|| !strncmp(*cpptr, FILECONTZPREFIX, strlen(FILECONTZPREFIX))){
	filecontentstobu = RENEWP(filecontentstobu, UChar *, i + 2);
	if(!filecontentstobu)
	  nomemerrexit();

	filecontentstobu[i] = *cpptr;
	filecontentstobu[i + 1] = NULL;
	if(filecontentstobu[i][0])
	  i++;

	for(cpptr2 = cpptr; *cpptr2; cpptr2++)
	  *(cpptr2) = *(cpptr2 + 1);
	cpptr--;
    }
  }

  /* open logfile */
  if(mode == MODE_FULL_BACKUP || mode == MODE_INCR_BACKUP){
    if(strcmp(logfile, "-"))
	lfp = fopen(logfile, "a");
  }

  if(lfp){
    switch(mode){
     case MODE_FULL_BACKUP:
      if(numparts > 1)
	logmsg("Starting full backup part %d", part);
      else
	logmsg("Starting %sfull backup", no_default_backup ?
				"customized " : "");
      break;

     case MODE_INCR_BACKUP:
      logmsg("Starting %sincremental backup", no_default_backup ?
				"customized " : "");
      break;

     default:
      break;
    }
  }

  if(no_default_backup || (mode == MODE_INCR_BACKUP && keep_counter))
    keep_timestamp = 1;

  /* read the number of the backup, if file present */
  if(mode == MODE_FULL_BACKUP){
    if(read_uns_file(numfile, &num))
      part = 1;

    if(part == 1 && ! keep_counter)
      num++;
  }

  /* construct the filename logfile */
  sprintf(tmpbuf, "%d", num);
  indexfile = strapp(indexfilepart, tmpbuf);
  if(!indexfile)
    nomemerrexit();

  /* before we're gonna really do i. e. modify anything, run init program */
  if(initprog){
    i = system(initprog);
    if(i){
	logmsg("Before program returned exit status %d. Stopping here", i);
	failed_exit(30);
    }
  }

  /* if it's a new backup, create the timestamp-file and save the old one */
  if(part == 1 && mode == MODE_FULL_BACKUP && !keep_timestamp){
    if(!access(oldmarkfile, R_OK)){
      unlink(orgoldmarkfile);
      if(rename(oldmarkfile, orgoldmarkfile)){
	errmsg("Error: Cannot rename file \"%s\"", oldmarkfile);
	failed_exit(9);
      }
    }

    fp = fopen(oldmarkfile, "w");
    if(!fp){
      errmsg("Error: Cannot create file \"%s\"", oldmarkfile);
      failed_exit(10);
    }
    fclose(fp);
  }

  if(mode == MODE_INCR_BACKUP && !keep_timestamp){
    fp = fopen(newmarkfile, "w");
    if(!fp){
      errmsg("Error: Cannot create file \"%s\"", newmarkfile);
      failed_exit(10);
    }
    fclose(fp);
  }

  if(interrupted)
    intrpt_exit();

  zippedidxf = strapp(indexfile, COMPRESS_SUFFIX);
  if(!access(zippedidxf, R_OK) && !access(indexfile, R_OK)){
    stat(indexfile, &statb);
    stat(zippedidxf, &statb2);

    if(statb.st_size == 0){
	unlink(indexfile);
    }
    if(statb2.st_size == 0){
	unlink(zippedidxf);
    }
  }

	/* If there are both files, this must be the result of a
	 * crash of this program (maybe, of course, due to a
	 * kill -9 or other mean kill-s). So the newer file MUST
	 * be the incomplete one and we can remove it, agree ?
	 */
  if(!access(zippedidxf, R_OK) && !access(indexfile, R_OK)){
    if(statb2.st_mtime > statb.st_mtime){
      unlink(zippedidxf);
    }
    else{
      unlink(indexfile);
    }
  }

  tmpidxf = strapp(indexfile, TMP_SUFFIX);
  unlink(tmpidxf);
  tmperrf = strapp(logfile, TMP_SUFFIX);
  unlink(tmperrf);

  infile = outfile = rename_from = rename_to = NULL;
  inunzip = outzip = 0;
  ifd = lfd = ipid = opid = errfd = -1;

  if(!access(indexfile, R_OK)){
    infile = (compresslogfiles ? indexfile : NULL);
    inunzip = 0;
  }

  if(!access(zippedidxf, R_OK)){
    inunzip = 1;

    if(compresslogfiles){
	infile = rename_to = zippedidxf;
    }
    else{
	infile = zippedidxf;
    }
  }
	
  outzip = (compresslogfiles && compresscmd[0] ? 1 : 0);
  outfile = (outzip ? (inunzip ? tmpidxf : zippedidxf) : indexfile);
  filelist = outfile;

  if(infile){
    cptr = inunzip ? (uncompresscmd[0] ? uncompresscmd : (UChar *) "gunzip") : NULL;
    ifd = open_from_pipe(cptr, infile, 1, &ipid);
    if(ifd < 0){
	errmsg("Warning: Cannot read filename logfile \"%s\"", infile);
    }
  }
  lfd = open_to_pipe(outzip ? compresscmd : NULL, outfile, 1 + 2, &opid, 0600);
  if(lfd < 0){
    errmsg("Error: Cannot write filename logfile \"%s\". Logging to stderr",
					outfile);
    lfd = 2;
    outfile = NULL;
  }

  num_inlines = 0;

  if(ifd >= 0 && lfd >= 0){
    while((i = read(ifd, tmpbuf, TMPBUFSIZ - 1)) > 0){
	write(lfd, tmpbuf, i);

	tmpbuf[i] = '\0';	/* count lines */
	cptr = tmpbuf;
	while( (cptr = strchr(cptr, '\n')) ){
	  num_inlines++;
	  cptr++;
	}
    }

    close(ifd);
    if(ipid >= 0){
      waitpid_forced(ipid, &pst, 0);

      if(WEXITSTATUS(pst)){
	errmsg("Warning: Errors occurred reading \"%s\"", infile);
      }
    }
  }

#if 0	/* This is nonsense. Filenames go to stderr !!! */
  /* open file for child (client) error log */
  errfd = open(tmperrf, O_WRONLY | O_CREAT | O_BINARY, 0600);
  if(errfd < 0){
    errmsg("Error: Cannot open file \"%s\" for client process error log.\n",
			tmperrf);
  }
#endif

  /* now we poll the servers, if there are several */
    /* We do this finally here, cause the program to find a ready backup */
    /* server should run within 5 seconds before the program to really */
    /* make the backup will run, cause then no other client can interfere */
    /* in the meantime (cause of preferred serving of repeated requests */
    /* from the same client within 5 - 6 seconds) */
  if(num_backuphosts > 1){
    forever{
      for(serveridx = 0; serveridx < num_backuphosts; serveridx++){
	if(interrupted)
	  break;

	n = get_streamerstate(&c, NULL, 0);
	if(n)
	  continue;
	i = c & STREAMER_STATE_MASK;

	if(i == STREAMER_READY)
	  break;

	if(i == STREAMER_UNLOADED && (c & STREAMER_CHANGEABLE)){
	  n = get_tapepos(&i, NULL, NULL, INSERT_POSITION, NULL, 0);
	  if(n)
	    continue;

	  compose_clntcmd(tmpbuf, NULL, backuphosts[serveridx],
				backupports[serveridx], cartsets[serveridx],
				cryptfile, i, 1, 0, 0, 0, NULL);
	  n = system(tmpbuf);
	  if(!n)
	    break;
	}
      }

      if(serveridx < num_backuphosts || interrupted)
	break;

      ms_sleep(1000 * 30);
    }
  }

  /* get startup information (actual writing position) */
  while(get_tapepos(&i, &j, NULL, INSERT_POSITION, NULL, 0)){
    if(interrupted)
	intrpt_exit();

    ms_sleep(1000 * 15);
    /*errmsg("Warning: Cannot get startup information");*/
  }
  inscartno = i;
  insfileno = j;

  if(interrupted)
    intrpt_exit();

  i = backupports[serveridx] >= 0 ? backupports[serveridx] : DEFAULT_PORT;
  sprintf(tmpbuf,
	"%s%s%s%d%s%d.%d:\n"
	"## %s backup at %s, current number: %d, hostname: %s\n",
		mode == MODE_INCR_BACKUP ? "\n" : "",
		backuphosts[serveridx], PORTSEP, (int) i, LOCSEP,
		(int) inscartno, (int) insfileno,
		mode == MODE_INCR_BACKUP ? "Incremental" : "Full",
		(char *) actimestr(), (int) num, systemname.nodename);
  write(lfd, tmpbuf, strlen(tmpbuf));	/* tmpbuf is used below !!! */

  /* now start the backup client subprocess */
  i = pipe(pp);
  if(!i){
    pid = fork_forced();
    if(pid < 0){
	errmsg("Error: Cannot start backup client side subprocess");
	failed_exit(12);
    }
    if(!pid){		/* child */
	char	**clargv, *header;
	char	portnostr[10], cartsetnostr[10];

			/* here tmpbuf is used from newline before ## */
	header = mk_esc_seq(strstr(tmpbuf, "##") - 1, '\\', NULL);
	if(!header)
	  exit(25);

	close(pp[1]);
	dup2(pp[0], 0);
	dup2(lfd, 1);
	dup2(errfd >= 0 ? errfd : lfd, 2);

	signal(SIGTERM, SIG_IGN);
	signal(SIGINT, SIG_IGN);

	clargv = NEWP(char *, 20 + num_dont_compress * 2);

	cpptr = (UChar **) clargv;
	*(cpptr++) = FN_BASENAME(clientprogram);
	*(cpptr++) = "-cvnlOE";
	*(cpptr++) = "-h";
	*(cpptr++) = backuphosts[serveridx];
	*(cpptr++) = "-H";
	*(cpptr++) = header;
	if(backupports[serveridx] >= 0){
	  *(cpptr++) = "-p";
	  sprintf(portnostr, "%d", (int) backupports[serveridx]);
	  *(cpptr++) = portnostr;
	}
	if(compressbu){
	  *(cpptr++) = "-z";
	  *(cpptr++) = compresscmd;
	  *(cpptr++) = uncompresscmd;
	}
	if(cryptfile){
	  *(cpptr++) = "-k";
	  *(cpptr++) = cryptfile;
	}
	if(dont_compress){
	  for(cpptr2 = dont_compress; *cpptr2; cpptr2++){
	    *(cpptr++) = "-s";
	    *(cpptr++) = *cpptr2;
	  }
	}
	if(cartsets[serveridx] > 1){
	  sprintf(cartsetnostr, "%d", (int) cartsets[serveridx]);
	  *(cpptr++) = "-S";
	  *(cpptr++) = cartsetnostr;
	}
	if(reportfile[0]){
	  *(cpptr++) = "-V";
	  *(cpptr++) = reportfile;
	}
	*cpptr = NULL;
	execv(clientprogram, clargv);

	do_exit(24);
    }
  }
  else{
    errmsg("Error: Cannot create pipe to subprocess");
    failed_exit(13);
  }

  close(pp[0]);
  if(outfile)
    close(lfd);
  if(errfd >= 0)
    close(errfd);

  *errflp = 0;

  if(waitpid(pid, &pst, WNOHANG) == pid){
    errmsg("Error: Backup client side subprocess unexpectedly died");
    failed_exit(28);
  }

  startnum = num;		/* store for later use (see below) */
  startcart = inscartno;
  startfile = insfileno;

  SETZERO(findparams);
  findparams.excl_dirs = dirstoskip;
  findparams.excl_names = filestoskip;
  findparams.errfunc = err_write_filename;
  findparams.errfunc_param = (void *) (pp + 1);
  findparams.excl_filename = exclude_filename;
  if(mode == MODE_INCR_BACKUP){
    stat(oldmarkfile, &statb);
    findparams.newer_than = statb.st_mtime;
  }

  success = 1;

  forever{	/* pseudo-loop */
    dirsarr[1] = NULL;

    for(cpptr = dirstobackup; *cpptr; cpptr++){
      dirsarr[0] = *cpptr;

      if(!strncmp(dirsarr[0], LOCALDEVPREFIX, i = strlen(LOCALDEVPREFIX))){
	findparams.options |= FIND_LOCAL_DEV;
	dirsarr[0] += i;
      }
      else{
	findparams.options &= ~FIND_LOCAL_DEV;
      }

      if(!dirsarr[0][0])
	continue;

      SETZERO(scandir_args);
      scandir_args.fd = pp[1];

      i = find(dirsarr, &findparams, write_filename, (void *) (& scandir_args));
      success = (success && (! i));

      if(interrupted)
	break;
    }

    if(interrupted)
    	break;

    i = 0;
    for(cpptr = filecontentstobu; *cpptr; cpptr++){
      if(! cpptr[0][0])
	continue;

      i |= write_filename(*cpptr, (void *) (pp + 1));

      if(interrupted)
	break;
    }

    success = (success && (! i));

    break;
  }

  close(pp[1]);
  waitpid_forced(pid, &pst, 0);
  *errflp |= WEXITSTATUS(pst);
  if(opid >= 0){
    waitpid_forced(opid, &pst, 0);
    *errflp |= WEXITSTATUS(pst);
  }

#if 0	/* This is nonsense */
  /* move error messages from the temporary error file to the logfile */
  errfp = fopen(tmperrf, "r");
  if(errfp){
    while(!feof(errfp) && (cptr = fget_alloc_str(errfp)) ){
	errmsg(cptr);
	free(cptr);
    }

    fclose(errfp);
  }
  unlink(tmperrf);
#endif

  if(logf_lost || *errflp)	/* This happens, when the child dies */
    must_read_fnamlog = 1;

  if(must_read_fnamlog){
    num_outlines = 0;
    if(inunzip || outzip){
      ipid = -1;			/* count written lines */
      uncompcmd = outzip ? (uncompresscmd[0] ? uncompresscmd : (UChar *) "gunzip") : NULL;
      filename = (inunzip && outzip) ? tmpidxf : outfile;
      i = stat(filename, &statb);
      if(i){
	errmsg("Warning: Cannot read filename logfile \"%s\"", filename);
      }
      if(!i && statb.st_size > 0){
	ifd = open_from_pipe(uncompcmd, filename, 1, &ipid);
	if(ifd < 0){
	  errmsg("Warning: Cannot read filename logfile \"%s\"", filename);
	}
	else{
	  fp = fdopen(ifd, "r");

	  while(!feof(fp)){
	    cptr = fget_alloc_str(fp);
	    if(!cptr)
		continue;

	    num_outlines++;

	    if(num_outlines > num_inlines &&
			INDEX_UNRECOGNIZED(eval_index_line(cptr,
					NULL, NULL, NULL, NULL, NULL))){
		fprintf(stderr, "%s", cptr);
	    }			/* print new error messages in log */

	    free(cptr);
	  }

	  fclose(fp);
	  if(ipid >= 0){
	    waitpid_forced(ipid, &pst, 0);
	    if(WEXITSTATUS(pst)){
		errmsg("Warning: Cannot find out number of lines written to \"%s\"",
				filename);
		num_outlines = 0;
	    }
	  }
	}
      }
    }

    if(num_outlines > num_inlines){	/* That's what it's all about: */
	logf_lost = 0;			/* new file is longer, so nothing */
    }					/* is lost. */
  }

  if(logf_lost){
    filelist = infile;

    if(inunzip && outzip){
	unlink(tmpidxf);
    }
    else{
      if(inunzip || outzip)
	unlink(outfile);
    }
  }
  else{
    if(inunzip && outzip){
	unlink(zippedidxf);
	rename(tmpidxf, zippedidxf);
	filelist = zippedidxf;
    }
    else{
      if(inunzip || outzip)
	unlink(infile);
    }
  }

  if(interrupted)
    intrpt_exit();

  if(! success){
    errmsg("Error: An error occurred during full backup");
    errmsg("The logfile \"%s\" and the command %s" FN_DIRSEPSTR "print_errors should tell details", logfile, bindir);
    failed_exit(14);
  }

  if(!success)
    failed_exit(15);

				/* now we know, that everything worked fine */
					/* now log the startup information */
  if(startinfoprog){
	i = backupports[serveridx] > 0 ? backupports[serveridx] : DEFAULT_PORT;
	fp = popen(startinfoprog, "w");
	fprintf(fp, "\n" EMREC_PREFIX "Backup:    %8d\n", startnum);
	fprintf(fp, EMREC_PREFIX "Server:    %8s\n", backuphosts[serveridx]);
	fprintf(fp, EMREC_PREFIX "Port:      %8d\n", (int) i);
	fprintf(fp, EMREC_PREFIX "Cartridge: %8d\n", startcart);
	fprintf(fp, EMREC_PREFIX "File:      %8d\n\n", startfile);
	pclose(fp);
  }

  if(mode == MODE_FULL_BACKUP){
    /* write the backup part number to file */
    if(numparts > 1){
      if(write_uns_file(partfile, part)){
	errmsg("Error: Cannot write to file \"%s\"", partfile);
	failed_exit(16);
      }

      logmsg("Full backup part %d finished", part);
    }
    else{
      logmsg("Full backup %sfinished", no_default_backup ?
				"(customized) " : "");
    }

    /* write the backup number to file */
    if(part == 1){
      if(write_uns_file(numfile, num)){
	errmsg("Error: Cannot write to file \"%s\"", numfile);
	failed_exit(8);
      }
    }

    if(!keep_timestamp)
	unlink(orgoldmarkfile);

    /* remove dated filename logfiles */
    if(numindexestostore >= 0){
      num -= (numindexestostore + 1);

      i = strlen(indexfile) + 30;
      if(i < TMPBUFSIZ)
	cptr = tmpbuf;
      else
	cptr = NEWP(UChar, i);
      if(cptr){
	while(num > 0){
	  sprintf(cptr, "%s%d", indexfilepart, num);
	  unlink(cptr);
	  strcat(cptr, COMPRESS_SUFFIX);
	  unlink(cptr);
	  num--;
	}
      }
    }
  }

  if(mode == MODE_INCR_BACKUP){
    if(!keep_timestamp){
      unlink(oldmarkfile);
      rename(newmarkfile, oldmarkfile);
    }

    logmsg("Incremental backup %sfinished", no_default_backup ?
				"(customized) " : "");
  }

  if(*errflp){
    errmsg("Warning: Minor errors occurred during backup. Try %s" FN_DIRSEPSTR "print_errors for details", bindir);
    errmsg("Also the file \"%s\" should supply additional information",
			logfile);
  }

  do_exit(0);
}

static int
open_idxfile_read(int * pid)
{
  UChar		*zippedname = NULL;
  int		fd, lpid;

  sprintf(tmpbuf, "%d", num);
  indexfile = strapp(indexfilepart, tmpbuf);

  lpid = fd = -1;
  if(!access(indexfile, R_OK)){
    fd = open(indexfile, O_RDONLY);
  }
  else{
    zippedname = strapp(indexfile, COMPRESS_SUFFIX);
    if(access(zippedname, R_OK))
	errmsg("Error: No filename logfile found");
    else
	fd = open_from_pipe(uncompresscmd[0] ? uncompresscmd : (UChar*)"gunzip",
			zippedname, 1, &lpid);
  }

  if(fd >= 0 && lpid >= 0)
    *pid = lpid;

  return(fd);
}

static void
compose_clntcmd(
  UChar		*cbuf,
  UChar		*flags,
  UChar		*backup_host,
  Int32		backup_port,
  Int32		cart_set,
  UChar		*keyfile,
  Int32		cartn,
  Int32		filen,
  time_t	time_older,
  time_t	time_newer,
  Int32		user_id,
  UChar		*restore_root)
{
  strcpy(cbuf, clientprogram);

  if(flags){
    if(flags[0]){
	strcat(cbuf, " -");
	strcat(cbuf, flags);
    }
  }

  if(!backup_host)
    backup_host = backuphosts[0];	/* use first server as default */

  strcat(cbuf, " -h ");
  strcat(cbuf, backup_host);

  if(backup_port > 0)			/* a port number must be > 0 */
    sprintf(cbuf + strlen(cbuf), " -p %d", (int) backup_port);

  if(keyfile)
    sprintf(cbuf + strlen(cbuf), " -k %s", keyfile);

  if(cart_set > 1)			/* 1 is default */
    sprintf(cbuf + strlen(cbuf), " -S %d", (int) cart_set);

  if(cartn > 0)
    sprintf(cbuf + strlen(cbuf), " -C %d", (int) cartn);

  if(filen > 0)
    sprintf(cbuf + strlen(cbuf), " -F %d", (int) filen);

  if(user_id != 0)
    sprintf(cbuf + strlen(cbuf), " -o %d", (int) user_id);

  if(restore_root)
    strcat(cbuf, " -r");

  if(time_older > 0){
    strcat(cbuf, " -B ");
    time_t_to_intstr(time_older, cbuf + strlen(cbuf));
  }

  if(time_newer > 0){
    strcat(cbuf, " -A ");
    time_t_to_intstr(time_newer, cbuf + strlen(cbuf));
  }
}

Int32
get_streamerstate(UChar * state, UChar * server, Int32 port)
{
  UChar		cbuf[1000], *cptr, *cptr2, *cptr3, *sstr;
  int		fd, pid, pst, i, n;

  *state = (UChar) 0;
  compose_clntcmd(cbuf, "w", backuphosts[serveridx], backupports[serveridx],
			0, cryptfile, 0, 0, 0, 0, 0, NULL);

  fd = open_from_pipe(cbuf, NULL, 1 + 2, &pid);
  if(fd >= 0){
    for(n = 0; (i = read(fd, cbuf + n, 999 - n)) > 0; n += (i > 0 ? i : 0));
    cbuf[n] = '\0';

    waitpid_forced(pid, &pst, 0);
    close(fd);
    if(WEXITSTATUS(pst)){
	return(1);
    }
    else{
	cptr = cbuf;
	forever{
	  if( (cptr2 = strchr(cptr, '\n')) )
	    *cptr2 = '\0';
	  if(strstr(cptr, "Streamer state:")){
	    cptr = sstr = strword(cptr, -1);

	    forever{
		if( (cptr3 = strchr(cptr, '+')) )
		  *cptr3 = '\0';
		*state |= (!strcasecmp(sstr, "BUSY") ? STREAMER_BUSY : (
			!strcasecmp(sstr, "READY") ? STREAMER_READY : (
			!strcasecmp(sstr, "UNLOADED") ? STREAMER_UNLOADED : (
			!strcasecmp(sstr, "DEVINUSE") ? STREAMER_DEVINUSE : (
			!strcasecmp(sstr, "UNAVAILABLE") ? STREAMER_UNAVAIL : (
			!strcasecmp(sstr, "CHANGEABLE") ? STREAMER_CHANGEABLE :
				STREAMER_UNKNOWN))))));
		if(!cptr3)
		  break;
		cptr = cptr3 + 1;
	    }

	    free(sstr);
	    return(0);
	  }

	  if(!cptr2)
	    break;
	  cptr = cptr2 + 1;
	}
    }
  }

  return(1);
}

Int32
get_tapepos(
  Int32		*n_cart,
  Int32		*n_file,
  Int32		*n_carts,
  Int8		mode,
  UChar		*server,
  Int32		port)
{
  UChar		cbuf[1000], *cptr, *cptr2;
  int		fd, pid, pst, i, n;
  long int	rcart = 0, rfile = 0, rncarts = 0;

  if(!server)
    server = backuphosts[serveridx];
  if(port <= 0)
    port = backupports[serveridx];

  compose_clntcmd(cbuf, NULL, server, port,
			cartsets[serveridx], cryptfile, 0, 0, 0, 0, 0, NULL);

  sprintf(cbuf + strlen(cbuf), " -%s%s",
		mode == INSERT_POSITION ? "Q" : "q", n_carts ? "v" : "");

  fd = open_from_pipe(cbuf, NULL, 1 + 2, &pid);
  if(fd >= 0){
    for(n = 0; (i = read(fd, cbuf + n, 999 - n)) > 0; n += (i > 0 ? i : 0));
    cbuf[n] = '\0';

    waitpid_forced(pid, &pst, 0);
    close(fd);
    if(WEXITSTATUS(pst)){
      return(1);
    }
    else{
	cptr = cbuf;
	forever{
	  if( (cptr2 = strchr(cptr, '\n')) )
	    *cptr2 = '\0';

	  if(re_match(&cartridge_re, cptr, strlen(cptr), 0, NULL) >= 0)
	    rcart = trailingint(cptr);

	  if(re_match(&file_re, cptr, strlen(cptr), 0, NULL) >= 0)
	    rfile = trailingint(cptr);

	  if(n_carts){
	    if(re_match(&ncarts_re, cptr, strlen(cptr), 0, NULL) >= 0)
	      rncarts = trailingint(cptr);
	  }

	  if(!cptr2)
	    break;
	  cptr = cptr2 + 1;
	}

	if(n_cart)
	  *n_cart = (Int32) rcart;
	if(n_file)
	  *n_file = (Int32) rfile;
	if(n_carts)
	  *n_carts = (Int32) rncarts;
	return(rcart ? 0 : 1);
    }
  }

  return(1);
}

static Int32
get_backup_args(int argc, char ** argv)
{
  Int8		clogf = 7, cbu = 7;
  Int32		num_unused = 0;
  UChar		**unused_args = NULL, *cptr;

  if(goptions(-argc, (UChar **) argv, "s:c;s:S;b:d;"
		"s:h;s:P;s:C;s:F;s:D;s:I;i:N;s2:z;s:s;s:X;s:l;"
		"b:a;s:i;s:b;s:e;+b:L;+b:B;s:k;s:V;b:v;*",
			&paramfile, &cartsetlist, &detach,
			&backuphostlist, &backupportlist,
			&rootdir, &filestoskipraw, &dirstoskipraw,
			&indexfilepart, &numindexestostore,
			&compresscmd, &uncompresscmd, &dont_compress_str,
			&exclude_filename, &logfile, &keep_counter,
			&startinfoprog, &initprog, &exitprog, &clogf, &cbu,
			&cryptfile, &vardir, &verbose,
			&num_unused, &unused_args
			))
    usage(argv[0]);

  if(num_unused > 0){
    dirstobackupraw = strdup("");
    for(num_unused--; num_unused >= 0; num_unused--){
	cptr = strchain("\"", unused_args[num_unused], "\" ",
					dirstobackupraw, NULL);
	free(dirstobackupraw);
	dirstobackupraw = cptr;
    }

    no_default_backup = 1;
    numparts = 1;
  }

  if(keep_counter)
    numparts = 1;

  if(clogf != 7)
    compresslogfiles = clogf;
  if(cbu != 7)
    compressbu = cbu;

  return(0);
}


static Int32
get_perror_args(int argc, char ** argv)
{
  int		i;
  UChar		have_prev = 0;

			/* option -p is handled in the filelist processing */
  i = goptions(-argc, (UChar **) argv,
    "b:0;b:1;b:2;b:3;b:4;b:5;b:6;b:7;b:8;b:9;s:c;s:I;i:N;s2:z;s:V;b:v",
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&paramfile, &indexfilepart, &numindexestostore,
	&compresscmd, &uncompresscmd, &vardir, &verbose);

  if(i)
    usage(argv[0]);

  if(have_prev)
    num_prev_backup = get_arg_num(argc, argv, &numidx, FIRST_NUM_ARG);

  return(0);
}

static Int32
get_verify_args(int argc, char ** argv)
{
  int		i, backupno, runno, n;
  UChar		have_prev = 0;

			/* option -p is handled in the filelist processing */
  i = goptions(-argc, (UChar **) argv,
    "b:0;b:1;b:2;b:3;b:4;b:5;b:6;b:7;b:8;b:9;b:.;s:c;s:S;s:h;s:P;"
		"s:C;s:I;s2:z;s:k;s:V;s:v",
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&have_prev, &paramfile, &cartsetlist, &backuphostlist, &backupportlist,
	&rootdir, &indexfilepart, &compresscmd, &uncompresscmd,
	&cryptfile, &vardir, &verbose);

  if(i)
    usage(argv[0]);

  if(have_prev){
    for(i = 1; i < argc; i++){
	n = sscanf(argv[i] + 1, "%d.%d", &runno, &backupno);
	if(n > 1)
	  num_prev_backup = backupno;
	if(n > 0){
	  num_prev_run = runno;
	  break;
	}
    }
  }

  return(0);
}

static void
print_errors(int c, char ** v)
{
  UChar		*line;
  int		fd, pid = -1, pst, i;
  FILE		*fp;

  if(num_prev_backup > 0)
    num -= num_prev_backup;

  fd = open_idxfile_read(&pid);
  fp = fdopen(fd, "r");
  if(!fp){
    errmsg("Error: Cannot read file \"%s\"", indexfile);
    do_exit(8);
  }

  fprintf(stdout, "Errors in backup # %d:\n\n", num);

  while(!feof(fp)){
    line = fget_alloc_str(fp);
    if(!line)
	break;

    i = eval_index_line(line, NULL, NULL, NULL, NULL, NULL);
    if(INDEX_UNRECOGNIZED(i))
      fprintf(stdout, "%s", line);

    free(line);
  }

  fclose(fp);

  if(pid >= 0){
    waitpid_forced(pid, &pst, 0);
    if(WEXITSTATUS(pst)){
      errmsg("Warning: Some errors occurred uncompressing \"%s\"", indexfile);
    }
  }

  do_exit(0);
}

static void
verifybu(int c, char ** v)
{
  UChar		*line, *server, **servers;
  int		fd, pid = -1, pst, i;
  Int32		*cnos = NULL, *fnos = NULL, listidx;
  Int32		cartn, filen;
  Int32		portn, *ports;
  FILE		*fp;

  if(num_prev_backup > 0)
    num -= num_prev_backup;

  cnos = NEWP(Int32, i = num_prev_run + 1);
  fnos = NEWP(Int32, i);
  servers = NEWP(UChar *, i);
  ports = NEWP(Int32, i);
  if(!cnos || !fnos || !servers || !ports)
    nomemerrexit();

  memset(servers, 0, sizeof(UChar *) * i);
  memset(ports, 0, sizeof(Int32) * i);
  for(i = 0; i <= num_prev_run; i++)
    cnos[i] = fnos[i] = -1;

  fd = open_idxfile_read(&pid);
  fp = fdopen(fd, "r");
  if(!fp){
    errmsg("Error: Cannot read file \"%s\"", indexfile);
    do_exit(8);
  }

  listidx = 0;
  while(!feof(fp)){
    line = fget_alloc_str(fp);
    if(!line)
	break;

    server = NULL;
    portn = -1;
    cartn = filen = -1;
    i = eval_index_line(line, &server, &portn, &cartn, &filen, NULL);
    if(INDEX_FILEENTRY(i) && !line[i]){
	cnos[listidx] = cartn;
	fnos[listidx] = filen;
	servers[listidx] = server;
	ports[listidx] = portn;
	listidx = (listidx + 1) % (num_prev_run + 1);
    }

    free(line);
  }
  fclose(fp);

  if(pid >= 0){
    waitpid_forced(pid, &pst, 0);
    if(WEXITSTATUS(pst)){
      errmsg("Warning: Some errors occurred uncompressing \"%s\"", indexfile);
    }
  }

  if(cnos[listidx] < 0 && fnos[listidx] < 0){
    errmsg("Error: Did not find start mark of previous backup in \"%s\"",
		indexfile);
    do_exit(9);
  }

  compose_clntcmd(tmpbuf, "d", servers[listidx], ports[listidx], 0, cryptfile,
				cnos[listidx], fnos[listidx], 0, 0, 0, NULL);

  system(tmpbuf);

  do_exit(0);
}

static int
get_arg_num(int argc, char ** argv, Int32 * idx, Int8 flags)
{
  static int	argp = 1;
  int		n, r = -1, i;

  if(flags & FIRST_NUM_ARG)
    argp = 1;

  for(; argp < argc; argp++){
    if(argv[argp][0] == '-'){
	i = sscanf(argv[argp] + 1, "%d%n", &r, &n);
	if(i > 0 && (flags & IGNORE_NUM_ARG_TRAILERS))
	  break;
	if(strlen(argv[argp] + 1) == n)
	  break;
    }
  }

  if(argp < argc && idx){
    *idx = argp;
  }
  argp++;

  return(r);
}

static Int32
get_restore_args(int argc, char ** argv)
{
  static Int8	reported = 0;
  Int32		i;
  UChar		have_prev = 0;

			/* option -p is handled in the filelist processing */
  i = goptions(-argc, (UChar **) argv,
    "b:0;b:1;b:2;b:3;b:4;b:5;b:6;b:7;b:8;b:9;b:n;b:l;s:h;s:R;s:C;b:a;b:e;"
		"b:f;b:E;s:B;s:A;s:c;s:S;*;s:P;s:I;s2:z;s:k;s:V;s:H;s:v",
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&do_counting, &do_listing, &backuphostlist, &restoreroot, &restoreroot,
	&restore_all, &restore_em, &restore_emlist, &restore_EM,
	&beforestr, &afterstr,
	&paramfile, &cartsetlist, &num_restore_paths, &restore_paths,
	&backupportlist, &indexfilepart, &compresscmd, &uncompresscmd,
	&cryptfile, &vardir, &srchost, &verbose);

  if(i)
    usage(argv[0]);

  if(num_restore_paths == 0 &&
		!restore_all && !restore_em && !restore_emlist && !restore_EM)
    usage(argv[0]);

  i = 0;
  if(restore_all)
    i++;
  if((do_listing || do_counting) && !restore_EM)
    i++;
  if(restore_em || restore_emlist)
    i++;
  if(restore_EM)
    i++;
  if(i > 1)
    usage(argv[0]);
  if(restore_em && restore_emlist && restore_EM)
    usage(argv[0]);
  if(have_prev && (restore_emlist || restore_em))
    usage(argv[0]);
  if(num_restore_paths > 0 &&
		(restore_emlist || restore_em || restore_all))
    usage(argv[0]);

  if(have_prev){
    num_prev_backup = get_arg_num(argc, argv, &i, FIRST_NUM_ARG);
    num_prev_backup_str = strdup(argv[i]);
  }

  if(afterstr){
    time_newer = time_from_datestr(afterstr);
    if(time_newer == (time_t) -1){
	fprintf(stderr, "Error: Cannot read date and/or time from \"%s\".\n",
			afterstr);
	usage(argv[0]);
    }

    if(!reported)
	fprintf(stdout, "Restoring files not older than: %s\n", ctime(&time_newer));
  }
  if(beforestr){
    time_older = time_from_datestr(beforestr);
    if(time_older == (time_t) -1){
	fprintf(stderr, "Error: Cannot read date and/or time from \"%s\".\n",
			beforestr);
	usage(argv[0]);
    }
    if(!reported)
	fprintf(stdout, "Restoring files not newer than: %s\n", ctime(&time_older));
  }

  reported = 1;

  return(0);
}

#define	startpattern	"^## [a-zA-Z]+ backup at [a-zA-Z0-9: ]+, "	\
		"current number: [1-9][0-9]*\\(,.*\\)?$"

static void
restore(int c, char ** v)
{
  static UChar		re_initialized = 0;
  static RE_cmp_buffer	startre;

  UChar		**cpptr, **cpptr2, **matches = NULL, *line, *cptr;
  UChar		**newmatches = NULL, added;
  int		i, n, num_matches = 0, fd, allocated;
  int		pst, pid;
  FILE		*fp;
  struct stat	statb;
  time_t	newtim, *starttimes = NULL, *endtimes = NULL;
  time_t	*newstarttimes = NULL, *newendtimes = NULL;

  if(!re_initialized){
    SETZERO(startre);
    re_compile_pattern(startpattern, strlen(startpattern), &startre);
    re_initialized = 1;
  }

  if(curuid){
    if(setuid(0)){
	errmsg("Error: Cannot setuid root, sorry.");
	do_exit(17);
    }
  }

  if(restoreroot){
    i = 0;

    if(curuid){
	i = access(restoreroot, R_OK);
	if(i){
	  errmsg("Error: Cannot read directory \"%s\". No read permission",
			restoreroot);
	  do_exit(20);
	}
	i = stat(restoreroot, &statb);
	if(i){
	  errmsg("Error: Cannot stat directory \"%s\"", restoreroot);

	  do_exit(21);
	}
	if(statb.st_uid != curuid){
	  errmsg("Error: The directory \"%s\" is not owned by you",
			restoreroot);

	  do_exit(22);
	}
    }

    rootdir = restoreroot;
  }

  /* change to the root directory */
  if(chdir(rootdir)){
    errmsg("Error: Cannot change to directory \"%s\"", rootdir);
    do_exit(7);
  }

  if(!srchost)
    srchost = strdup(systemname.nodename);
  cptr = strchr(srchost, '.');
  if(cptr)
    *cptr = '\0';

  if(restore_em || restore_emlist || restore_EM)
    restoreem(c, v);

  for(i = 0; i < num_restore_paths; i++){
    if(!strcmp(restore_paths[i], "-p")){
	num_restore_paths--;
	free(restore_paths[i]);
	for(n = i; n < num_restore_paths; n++)
	  restore_paths[n] = restore_paths[n + 1];
    }
    else{
	cptr = strchain("*", restore_paths[i], "*", NULL);
	free(restore_paths[i]);
	restore_paths[i] = cptr;
    }
  }

  have_timerange = (time_older > 0 || time_newer > 0);

  indexfiles = NEWP(UChar *, 2);
  zindexfiles = NEWP(UChar *, 2);
  if(!indexfiles || !zindexfiles)
    nomemerrexit();
  memset(indexfiles, 0, 2 * sizeof(UChar *));
  memset(zindexfiles, 0, 2 * sizeof(UChar *));

  if(!have_timerange){
    if(num_prev_backup > 0){
      num -= num_prev_backup;
      sprintf(tmpbuf, "%d", num);
      indexfiles[0] = strapp(indexfilepart, tmpbuf);
    }
    else{
      if(read_uns_file(partfile, &part))
	part = 1;

      if(part < numparts && num > 1){
	sprintf(tmpbuf, "%d", num - 1);
	indexfiles[0] = strapp(indexfilepart, tmpbuf);
	sprintf(tmpbuf, "%d", num);
	indexfiles[1] = strapp(indexfilepart, tmpbuf);
	zindexfiles[1] = strapp(indexfiles[1], COMPRESS_SUFFIX);
      }
      else{
	sprintf(tmpbuf, "%d", num);
	indexfiles[0] = strapp(indexfilepart, tmpbuf);
      }
    }
    zindexfiles[0] = strapp(indexfiles[0], COMPRESS_SUFFIX);
  }
  else{
    num_indexfiles = 0;

    sprintf(tmpbuf, "%s%d", indexfilepart, num);
    forever{
      cptr = tmpbuf + strlen(tmpbuf) + 2;
      sprintf(cptr, "%s%s", tmpbuf, COMPRESS_SUFFIX);

      num_indexfiles++;

      indexfiles = RENEWP(indexfiles, UChar *, num_indexfiles + 1);
      zindexfiles = RENEWP(zindexfiles, UChar *, num_indexfiles + 1);
      indexfiles[num_indexfiles - 1] = strdup(tmpbuf);
      zindexfiles[num_indexfiles - 1] = strdup(cptr);
      indexfiles[num_indexfiles] = zindexfiles[num_indexfiles] = NULL;

      num--;
      sprintf(tmpbuf, "%s%d", indexfilepart, num);
      if(!access(tmpbuf, R_OK))
	continue;
      cptr = tmpbuf + strlen(tmpbuf) + 2;
      sprintf(cptr, "%s%s", tmpbuf, COMPRESS_SUFFIX);
      if(!access(cptr, R_OK))
	continue;

      break;
    }

    for(i = 0; i < num_indexfiles / 2; i++){
	memswap(indexfiles + i, indexfiles + num_indexfiles - 1 - i,
				sizeof(UChar *));
	memswap(zindexfiles + i, zindexfiles + num_indexfiles - 1 - i,
				sizeof(UChar *));
    }
  }

  for(cpptr = indexfiles, i = 0; *cpptr; cpptr++, i++){
    if(access(*cpptr, R_OK) && access(zindexfiles[i], R_OK)){
	errmsg("Serious warning: Cannot read filename logfile \"%s\"", *cpptr);
	free(*cpptr);
	free(zindexfiles[i]);

	for(cpptr2 = cpptr, n = i; *cpptr2; cpptr2++, n++){
	  *cpptr2 = *(cpptr2 + 1);
	  zindexfiles[n] = zindexfiles[n + 1];
	}

	cpptr--;
	i--;
    }
  }

  if(restore_all)
    restoreall(c, v);

  line = NULL;
  allocated = 0;
  current_bu_time = 0;
  prev_bu_time = 0;

  for(cpptr = indexfiles; *cpptr; cpptr++){
    pid = -1;
    fd = open(*cpptr, O_RDONLY);
    if(fd < 0){
      if((fd = open_from_pipe(uncompresscmd[0] ? uncompresscmd : (UChar*)"gunzip",
		zindexfiles[cpptr - indexfiles], 1, &pid)) < 0)
	continue;
    }
    fp = fdopen(fd, "r");
    if(!fp){
	errmsg("Error: Cannot read file \"%s\"", *cpptr);
	continue;
    }
    while(!feof(fp)){
      if(line)
	free(line);
      line = fget_alloc_str(fp);
      if(!line)
	continue;

      cptr = line + strlen(line) - 1;
      if(*cptr == '\n')
	*cptr = '\0';

      i = eval_index_line(line, NULL, NULL, NULL, NULL, NULL);
      if(INDEX_COMMENT(i)){
	if(!strncmp(cptr = first_nospace(line), "##", 2)){
	  if(!read_header_line(cptr, NULL, &newtim, NULL, NULL)){
	    prev_bu_time = current_bu_time;
	    current_bu_time = newtim;

	    if(have_timerange){
		for(i = num_matches - 1; i >= 0; i--){
		  if(endtimes[i] != (time_t) -1)
		    break;

		  endtimes[i] = current_bu_time;
		}
	    }
	  }
        }
	continue;
      }

      if(!line[i])
	continue;

      cptr = line + i;

      for(i = 0; i < num_restore_paths; i++){
	if(!fnmatch(restore_paths[i], cptr, FNM_PATHNAME)){
	  added = 0;

	  newmatches = NEWP(UChar *, num_matches + 1);
	  if(have_timerange){
	    newstarttimes = NEWP(time_t, num_matches + 1);
	    newendtimes = NEWP(time_t, num_matches + 1);
	  }
	  if(!newmatches || (have_timerange
				&& (!newstarttimes || !newendtimes))){
	    ZFREE(newmatches);
	    ZFREE(newstarttimes);
	    ZFREE(newendtimes);

	    if(num_matches < 10)
		nomemerrexit();

	    if(have_timerange)
		matches_check_timerange(matches, &num_matches, starttimes,
					endtimes, time_newer, time_older);

	    write_to_restore(matches, num_matches, 0);

	    matches = NULL;
	    num_matches = allocated = 0;
	    ZFREE(starttimes);
	    ZFREE(endtimes);

	    newmatches = NEWP(UChar *, 1);
	    if(have_timerange){
		newstarttimes = NEWP(time_t, 1);
		newendtimes = NEWP(time_t, 1);
	    }
	    if(!newmatches || (have_timerange
				 && (!newstarttimes || !newendtimes)))
		nomemerrexit();
	  }

	  if(matches){
	    memcpy(newmatches, matches, num_matches * sizeof(UChar *));

	    free(matches);
	  }
	  if(have_timerange && starttimes && endtimes){
	    memcpy(newstarttimes, starttimes, num_matches * sizeof(time_t));
	    memcpy(newendtimes, endtimes, num_matches * sizeof(time_t));

	    free(starttimes);
	    free(endtimes);
	  }

	  matches = newmatches;
	  matches[num_matches] = strdup(line);
	  if(have_timerange){
	    starttimes = newstarttimes;
	    starttimes[num_matches] = prev_bu_time;
	    endtimes = newendtimes;
	    endtimes[num_matches] = (time_t) -1;
	  }
	  if(matches[num_matches]){
	    num_matches++;
	    added = 1;
	    allocated += strlen(line) + 1;
	  }
	  if(allocated > 200000 || !added){
	    if(have_timerange)
		matches_check_timerange(matches, &num_matches, starttimes,
					endtimes, time_newer, time_older);

	    write_to_restore(matches, num_matches, 0);
	    ZFREE(starttimes);
	    ZFREE(endtimes);

	    matches = NEWP(UChar *, 1);
	    if(have_timerange){
		starttimes = NEWP(time_t, 1);
		endtimes = NEWP(time_t, 1);
	    }
	    if(!matches || (have_timerange
				&& (!starttimes || !endtimes)))
		nomemerrexit();
	    if(!added){
	      matches[0] = strdup(line);
	      if(!matches[0])
		nomemerrexit();
	    }
	    num_matches = added ? 0 : 1;
	    allocated = added ? 0 : strlen(line) + 1;
	  }

	  break;
	}
      }
    }

    fclose(fp);
    if(pid >= 0){
      waitpid_forced(pid, &pst, 0);

      if(WEXITSTATUS(pst)){
	errmsg("Warning: Uncompressing \"%s\" returned bad exit status",
			*(cpptr + 3));
      }
    }
  }

  if(have_timerange){
    current_bu_time = time(NULL);

    for(i = num_matches - 1; i >= 0; i--){
	if(endtimes[i] != (time_t) -1)
	  break;

	endtimes[i] = current_bu_time;
    }
  }

  if(have_timerange)
    matches_check_timerange(matches, &num_matches, starttimes,
				endtimes, time_newer, time_older);

  write_to_restore(matches, num_matches, 1);

  ZFREE(starttimes);
  ZFREE(endtimes);

  do_exit(0);
}

static	Int32	total_num_matches = 0, total_nc = 0, total_nf = 0,
		total_ns = 0;

static Int32
write_to_restore(UChar ** matches, Int32 num_matches, UChar last_one)
{
  int		i, j, fileuid, fd, pid, pst;
  Int32		cartno, fileno, portno, *cartnos, *filenos, *efilenos;
  Int32		*portnos, actcart, actfile, num_sections;
  Int32		actport, nf, nc, ns, n;
  UChar		*cptr, **cpptr, showit, warned_old_log = 0, **servers;
  UChar		*server, *actserver, next_section;

  if(num_matches <= 0)
    return(0);

  cartnos = NEWP(Int32, 1);
  filenos = NEWP(Int32, 1);
  efilenos = NEWP(Int32, 1);
  portnos = NEWP(Int32, 1);
  servers = NEWP(UChar *, 1);
  cpptr = NEWP(UChar *, num_matches);
  if(!cartnos || !filenos || !efilenos || !portnos || !servers || !cpptr)
    nomemerrexit();

  if(!have_timerange){
    for(i = 0; i < num_matches; i++){	/* eliminate duplicates */
      j = eval_index_line(matches[i], NULL, NULL, NULL, NULL, NULL);
      cpptr[i] = matches[i] + j;
/*      while(isspace(*(cpptr[i])))
	cpptr[i]++;*/ /* unnecessary. eval_index_line should do this */
    }
    for(i = num_matches - 1; i > 0; i--){
      for(j = i - 1; j >= 0; j--){
	if(!strcmp(cpptr[i], cpptr[j])){
	  num_matches--;
	  free(matches[j]);
	  memmove(matches + j, matches + j + 1,
				(num_matches - j) * sizeof(matches[0]));
	  memmove(cpptr + j, cpptr + j + 1,
				(num_matches - j) * sizeof(matches[0]));
	  i--;
	}
      }
    }
  }

  free(cpptr);

  cpptr = matches;

  num_sections = nc = nf = ns = 0;
  actcart = actfile = actport = -1;
  actserver = server = NULL;

  for(i = 0; i < num_matches; i++){
    ZFREE(server);		/* they may miss, so we have to preset them */
    portno = -1;
    j = eval_index_line(matches[i], &server, &portno, &cartno, &fileno, NULL);

    next_section = 0;

    if((server && actserver && strcmp(server, actserver)) || portno != actport){
	ns++;
	nc++;
	nf++;
	next_section = 1;
	ZFREE(actserver);
	if(server)
	  actserver = strdup(server);
    }
    else if(actcart != cartno){
	nc++;
	nf++;
	next_section = 1;
    }
    else if(actfile != fileno && actfile + 0 != fileno){
	nf++;
	next_section = 1;
    }

    if(next_section){
	cartnos = RENEWP(cartnos, Int32, n = num_sections + 1);
	filenos = RENEWP(filenos, Int32, n);
	portnos = RENEWP(portnos, Int32, n);
	efilenos = RENEWP(efilenos, Int32, n);
	servers = RENEWP(servers, UChar *, n);
	if(!cartnos || !filenos || !efilenos || !portnos || !servers)
	  nomemerrexit();

	cartnos[num_sections] = cartno;
	filenos[num_sections] = fileno;
	efilenos[num_sections] = fileno;
	portnos[num_sections] = portno;
	servers[num_sections] = server ? server : (UChar *) strdup(backuphosts[0]);
	server = NULL;

	num_sections++;
    }

    actcart = cartno;
    actfile = fileno;
    actport = portno;
    efilenos[num_sections - 1] = fileno;
  }

  ZFREE(server);
  ZFREE(actserver);

  if(do_counting || do_listing){
    total_num_matches += num_matches;
    total_nf += nf;
    total_nc += nc;
    total_ns += ns;

    if(do_listing){
      for(i = 0; i < num_matches; i++){
	fileuid = 0;

        j = eval_index_line(matches[i], NULL, NULL, NULL, NULL, &fileuid);
	cptr = matches[i] + j;
	if(*cptr){
	  showit = 1;

	  if(curuid){
	    if(!fileuid){
	      if(!warned_old_log){
		errmsg("Warning: Filelist without user-ID information. Not displaying contents");
		warned_old_log = 1;
	      }
	      showit = 0;
	    }
	    else{
	      if(fileuid != curuid)
		showit = 0;
	    }
	  }

	  if(showit)
	    fprintf(stdout, "%s\n", cptr);
	}

	free(matches[i]);
      }
    }

    if(last_one){
      if(do_counting)
	fprintf(stdout,
		"Found %d matches in %d files on %d tapes on %d servers.\n",
		(int) total_num_matches, (int) total_nf, (int) total_nc,
		(int) total_ns);

      total_num_matches = total_nf = total_nc = 0;
    }

    CLEANUP;
  }

  fprintf(stdout, "Found %d matches in %d files on %d tapes on %d servers.\n",
			(int) num_matches, (int) nf, (int) nc, (int) ns);

  if(!last_one)
    fprintf(stdout,
	"(This is not the last set of files, please be patient)\n");

  actfile = filenos[0];
  actcart = cartnos[0];
  actport = portnos[0];
  actserver = strdup(servers[0]);
  for(n = 0; n < num_sections; n++){
    compose_clntcmd(tmpbuf, "xvgu", servers[n], portnos[n], 0, cryptfile,
			cartnos[n], filenos[n], time_older, time_newer,
				curuid, restoreroot);

    if( (fd = open_to_pipe(tmpbuf, NULL, 1 + 2, &pid, 0)) < 0){
      errmsg("Error: Cannot start client subprocess");
      do_exit(18);
    }

    if(filenos[n] == efilenos[n])
	fprintf(stdout,
		"Restoring files from server %s, port %d, cartridge %d, file %d\n",
					servers[n], (int) portnos[n],
					(int) cartnos[n], (int) filenos[n]);
    else
	fprintf(stdout,
		"Restoring files from server %s, port %d, cartridge %d, file %d - %d\n",
				servers[n], (int) portnos[n], (int) cartnos[n],
					(int) filenos[n], (int) efilenos[n]);

    while(cpptr < matches + num_matches){	/* skip further matches */
	ZFREE(server);		/* they may lack, so we have to preset them */
	portno = -1;
	i = eval_index_line(*cpptr, &server, &portno, &cartno, &fileno, NULL);

	if((server && actserver && strcmp(server, actserver))
			|| portno != actport || actcart != cartno
			|| (fileno != actfile && fileno != actfile + 0))
	  break;
	actfile = fileno;	/* these 3 lines are basically superfluous */
	actcart = cartno;	/* have to re-think when i find time */
	actport = portno;

	cptr = *cpptr + i;	/* this is cool */
	if(*cptr)
	  write_filename(cptr, (void *) (&fd));

	free(*cpptr);
	cpptr++;
    }
    actfile = fileno;
    actcart = cartno;
    actport = portno;
    ZFREE(actserver);
    actserver = server;
    server = NULL;

    close(fd);
    waitpid_forced(pid, &pst, 0);

    if(WEXITSTATUS(pst)){
	fprintf(stderr, "Errors occurred during restore. See previous output for details.\n");
    }

    i = portnos[n] >= 0 ? portnos[n] : DEFAULT_PORT;
    if(filenos[n] == efilenos[n])
	fprintf(stdout, "Done with server %s, port %d, cartridge %d, file %d\n",
			servers[n], i, (int) cartnos[n], (int) filenos[n]);
    else
	fprintf(stdout,
		"Done with server %s, port %d, cartridge %d, file %d - %d\n",
				servers[n], i, (int) cartnos[n],
				(int) filenos[n], (int) efilenos[n]);

    ZFREE(servers[n]);
  }

 cleanup:
  free(servers);
  free(portnos);
  free(cartnos);
  free(filenos);
  free(efilenos);

  free(matches);

  return(0);
}

static void
restoreall(int c, char ** v)
{
  UChar		**cpptr, *cptr, *line = NULL, *prevlast = NULL;
  UChar		server_changed, found;
  UChar		*filename, *line2 = NULL, *server = NULL, *oldserver = NULL;
  FILE		*fp, *cfp;
  int		fd, pid, pst, n, cpid, cfd;
  Int32		cartno, fileno, oldcart, oldfile, portno, oldport;

  oldcart = oldfile = oldport = -1;

  for(cpptr = indexfiles; *cpptr; cpptr++){
    pid = -1;
    fd = open(*cpptr, O_RDONLY);
    if(fd < 0){
      if((fd = open_from_pipe(uncompresscmd[0] ? uncompresscmd : (UChar*)"gunzip",
			zindexfiles[cpptr - indexfiles], 1, &pid)) < 0)
	continue;
    }
    fp = fdopen(fd, "r");
    if(!fp){
	errmsg("Error: Cannot read file \"%s\"", *cpptr);
	continue;
    }
    found = 0;
    while(!feof(fp)){	/* search input logfile lines */
      if(line)
	free(line);
      line = fget_alloc_str(fp);
      if(!line)
	continue;

      ZFREE(server);
      portno = -1;
      n = eval_index_line(line, &server, &portno, &cartno, &fileno, NULL);
      if(!INDEX_FILEENTRY(n))
	continue;
      if(!line[n])
	continue;

      server_changed = (server && ! oldserver) || (!server && oldserver)
			|| (server && oldserver && strcmp(server, oldserver));

	/* in this case error messages appear at end of client output */
      if(found && cartno == oldcart && fileno == oldfile && oldport == portno
					&& !server_changed)
	continue;

      oldport = portno;
      oldcart = cartno;
      oldfile = fileno;
      if(server_changed){
	ZFREE(oldserver);
	oldserver = server;
	server = NULL;
      }

      cptr = line + strlen(line) - 1;
      if(*cptr == '\n')
	*cptr = '\0';
      filename = line + n;

      if(!prevlast || found){	/* if found of first line at all: restore */
	compose_clntcmd(tmpbuf, "xvaunl", oldserver, portno, 0, cryptfile,
				cartno, fileno, time_older, time_newer,
					curuid, restoreroot);

	if( (cfd = open_from_pipe(tmpbuf, NULL, 1 + 2, &cpid)) < 0){
	  errmsg("Error: Cannot start client subprocess");
	  continue;
	}

	cfp = fdopen(cfd, "r");
	if(!cfp){
	  errmsg("Error: Cannot read from restore subprocess");
	  do_exit(34);
	}

	fprintf(stdout,
		"Going to restore from server %s, port %d, cartridge %d, file %d\n",
				server ? server : backuphosts[0], (int) portno,
						(int) cartno, (int) fileno);
	while(!feof(cfp)){
	  if(line2)
	    free(line2);
	  line2 = fget_alloc_str(cfp);
	  if(!line2)
	    continue;

	  fprintf(stdout, "%s", line2);

	  n = eval_index_line(line2, NULL, NULL, NULL, NULL, NULL);
	  if(!INDEX_FILEENTRY(n))
	    continue;
	  if(!line2[n])
	    continue;

	  cptr = line2 + strlen(line2) - 1;
	  if(*cptr == '\n')
	  *cptr = '\0';

	  cptr = line2 + n;

	  if(prevlast)
	    free(prevlast);
	  prevlast = strdup(cptr);
	}

	fclose(cfp);
	waitpid_forced(cpid, &pst, 0);
	if(WEXITSTATUS(pst))
	  fprintf(stderr, "Errors occurred during restore. See previous output for details.\n");

	found = 0;
      }

      if(!prevlast)
	break;

      if(!strcmp(prevlast, filename))
	found = 1;
    }

    fclose(fp);
    if(pid >= 0){
      waitpid_forced(pid, &pst, 0);
      if(WEXITSTATUS(pst)){
	fprintf(stderr,
		"Warning: Uncompressing \"%s\" returned bad exit status.\n",
			*(cpptr + 3));
      }
    }

    ZFREE(prevlast);
  }

  do_exit(0);
}

void
matches_check_timerange(
  UChar		**matches,
  int		*num_matches,
  time_t	*starttimes,
  time_t	*endtimes,
  time_t 	time_newer,
  time_t	time_older)
{
  int		idx1, idx2;

  if(! time_newer && ! time_older)
    return;

  for(idx1 = idx2 = 0; idx2 < *num_matches; idx1++, idx2++){
    if((time_newer && endtimes[idx2] < time_newer
				&& endtimes[idx2] != (time_t) -1)
		|| (time_older && starttimes[idx2] > time_older
				&& starttimes[idx2] != (time_t) -1)){
	free(matches[idx2]);
	idx1--;
    }
    else{
	matches[idx1] = matches[idx2];
	starttimes[idx1] = starttimes[idx2];
	endtimes[idx1] = starttimes[idx2];
    }
  }

  *num_matches = idx1;
}

Int32
read_header_line(
  UChar		*line,
  Int32		*mode,
  time_t	*starttime,
  Int32		*bunum,
  UChar		*hostname)
{
  static UChar		re_initialized = 0;
  static RE_cmp_buffer	startre;

  time_t	read_time;
  Int32		read_mode;
  int		read_num, i;
  UChar		*cptr;

  if(!re_initialized){
    SETZERO(startre);
    re_compile_pattern(startpattern, strlen(startpattern), &startre);
    re_initialized = 1;
  }

  if(re_find_match(&startre, line, NULL, NULL) == 0){
    while(!isspace(*line))
	line++;
    line = first_nospace(line);

    if(!strncasecmp(line, "inc", 3))
	read_mode = MODE_INCR_BACKUP;
    else if (!strncasecmp(line, "full", 4))
	read_mode = MODE_FULL_BACKUP;
    else
	return(1);

    if(mode)
	*mode = read_mode;

    for(i = 0; i < 3; i++){
	while(!isspace(*line))
	  line++;
	line = first_nospace(line);
    }
    cptr = strchr(line, ',');
    *cptr = '\0';

    read_time = time_from_datestr(line);
    if(read_time == (time_t) -1)
	return(1);
    if(starttime)
	*starttime = read_time;

    *cptr = ',';
    line = cptr + 1;
    cptr = "current number:";
    line = strstr(line, cptr) + strlen(cptr);
    sscanf(line, "%d", &read_num);
    if(bunum)
	*bunum = (Int32) read_num;

    if(!hostname)
	return(0);
    cptr = "hostname:";
    line = strstr(line, cptr);
    if(!line){
	if(hostname)
	  strcpy(hostname, "???");
	return(0);
    }
    line = first_nospace(line + strlen(cptr));
    if(hostname)
	sscanword(line, hostname);

    return(0);
  }

  return(1);
}

static int
trailingint(UChar * str)
{
  int	mul = 1, r = 0;

  str += strlen(str) - 1;
  while(isspace(*str))
    str--;
  while(isdigit(*str)){
    r += mul * (*str - '0');
    mul *= 10;
    str--;
  }

  return(r);
}

static Int32
cmp_tapeEntry_loc(void * l1, void * l2)
{
  BuTapeEntry	*e1, *e2;
  UChar		*s1, *s2;
  Int32		c, p1, p2;

  e1 = (BuTapeEntry *) l1;
  e2 = (BuTapeEntry *) l2;

  s1 = e1->server ? e1->server : backuphosts[serveridx];
  s2 = e2->server ? e2->server : backuphosts[serveridx];

  c = strcmp(s1, s2);
  if(c)
    return(c);

  p1 = e1->port > 0 ? e1->port : 0;
  p2 = e2->port > 0 ? e2->port : 0;
  if(p1 != p2)
    return(p1 - p2);

  return(e1->cart - e2->cart);
}

static Int32
cmp_tapeEntry_time(void * p1, void * p2)
{
  time_t	t1, t2;
  Int32		num1, num2;

  num1 = ((BuTapeEntry *) p1)->bunum;
  num2 = ((BuTapeEntry *) p2)->bunum;

  if(num1 != num2)
    return(num1 > num2 ? 1 : -1);

  t1 = ((BuTapeEntry *) p1)->budate;
  t2 = ((BuTapeEntry *) p2)->budate;

  return(t1 > t2 ? 1 : (t1 < t2 ? -1 : 0));
}

#define	num_prev_carts		num_prev_backup
#define	num_prev_carts_str	num_prev_backup_str
#define	cartranges		restore_paths
#define	num_cartranges		num_restore_paths
#define	dont_restore		do_counting

static Int32
find_tapepos(
  Int32		*n_cart,
  Int32		*n_file,
  Int32		*n_carts,
  Int8		mode,
  UChar		*server,
  Int32		port)
{
  static Int32		num_buf_entries = 0;
  static BuTapeEntry	*buf_entries = NULL;

  Int32		i, nc, nf, ncs;

  if(!n_cart && !n_file && ! n_carts && mode == CLEAR_LIST){
    num_buf_entries = 0;
    return(0);
  }

  if(!server)
    server = backuphosts[serveridx];
  if(port <= 0)
    port = backupports[serveridx];
  if(port <= 0)
    port = 0;

  for(i = 0; i < num_buf_entries; i++){
    if(buf_entries[i].port == port && !strcmp(buf_entries[i].server, server)){
	if(buf_entries[i].bunum <= 0)
	  continue;
	if(n_carts)
	  *n_carts = buf_entries[i].bunum;

	if(mode == INSERT_POSITION){
	  if(buf_entries[i].inscart <= 0 || buf_entries[i].insfile <= 0)
	    continue;

	  if(n_cart)
	    *n_cart = buf_entries[i].inscart;
	  if(n_file)
	    *n_file = buf_entries[i].insfile;
	}

	if(mode == ACCESS_POSITION){
	  if(buf_entries[i].cart <= 0 || buf_entries[i].file <= 0)
	    continue;

	  if(n_cart)
	    *n_cart = buf_entries[i].cart;
	  if(n_file)
	    *n_file = buf_entries[i].file;
	}

	return(0);
    }
  }

  i = get_tapepos(&nc, &nf, &ncs, mode, server, port);
  if(i)
    return(i);

  buf_entries = ZRENEWP(buf_entries, BuTapeEntry, num_buf_entries + 1);
  if(!buf_entries)
    return(-1);

  SETZERO(buf_entries[num_buf_entries]);

  buf_entries[num_buf_entries].server = strdup(server);
  buf_entries[num_buf_entries].port = port;
  buf_entries[num_buf_entries].bunum = ncs;

  if(mode == INSERT_POSITION){
    buf_entries[num_buf_entries].inscart = nc;
    buf_entries[num_buf_entries].insfile = nc;
  }
  if(mode == ACCESS_POSITION){
    buf_entries[num_buf_entries].cart = nc;
    buf_entries[num_buf_entries].file = nf;
  }

  num_buf_entries++;

  if(n_carts)
    *n_carts = ncs;
  if(n_cart)
    *n_cart = nc;
  if(n_file)
    *n_file = nf;

  return(0);
}

Int32
find_streamerstate(UChar * state, UChar * server, Int32 port)
{
  static Int32		num_buf_entries = 0;
  static BuTapeEntry	*buf_entries = NULL;

  UChar		actstate;
  Int32		i;

  if(!state){
    num_buf_entries = 0;
    return(0);
  }

  if(!server)
    server = backuphosts[serveridx];
  if(port <= 0)
    port = backupports[serveridx];
  if(port <= 0)
    port = 0;

  for(i = 0; i < num_buf_entries; i++){
    if(buf_entries[i].port == port && !strcmp(buf_entries[i].server, server)){
	if(buf_entries[i].mode < 0)
	  continue;

	*state = (UChar) buf_entries[i].mode;
	return(0);
    }
  }

  if( (i = get_streamerstate(&actstate, server, port)) )
    return(i);

  buf_entries = ZRENEWP(buf_entries, BuTapeEntry, num_buf_entries + 1);
  if(!buf_entries)
    return(-1);

  SETZERO(buf_entries[num_buf_entries]);

  buf_entries[num_buf_entries].server = strdup(server);
  buf_entries[num_buf_entries].port = port;
  buf_entries[num_buf_entries].mode = (Int32) actstate;

  num_buf_entries++;

  *state = actstate;

  return(0);
}

static void
restoreem(int c, char ** v)
{
  UChar		**lines = NULL, *line = NULL, *zindexfile, **servers = NULL;
  UChar		*cptr, *cptr2, noservavail, actstate;
  int		i, j, k, l, num_lines = 0, prev_bunum = -1, act_bunum = -1;
  FILE		*fp;
  int		fd, pid, opid, pst, ofd;
  Int32		num_backups, *carts, *files, *ports, startidx, stopidx;
  Int32		num_search_entries = 0, idx;
  Int32		actinscart, inscart, num_tape_entries = 0, num_to_search;
  Int32		latest_bunum, earliest_bunum, numcarts = 1, actcart, f;
  Int32		latest_bunum_cart, earliest_bunum_cart;
  struct stat	statb;
  BuTapeEntry	*tape_entries = NULL, act_tape_entry, read_tape_entry;
  BuTapeEntry	*search_entries = NULL, *tmpentries;
  UName		bu_ownerhost;

  if(curuid){
    errmsg("Error: Emergency restore may only be performed by root");
    do_exit(19);
  }

  if(restore_EM){		/* emergency restore without min info */
    if(num_cartranges){		/* evaluate args starting with a number */
	for(i = 0; i < num_cartranges; i++){
	  if(sscanf(cartranges[i], "%d-%d", &j, &k) < 2){	/* x-y ? */
	    if(sscanf(cartranges[i], "%d", &j) <= 0){		/* no, x */
		errmsg("Warning: Cannot classify option \"%s\", ignored",
						cartranges[i]);
		continue;
	    }

	    if(j < 0){		/* erroneously found an additional arg. */
		num_prev_carts++;	/* we simply ignore it and let */
		continue;		/* the next loop process it - */
	    }			/* makes life easier, as it's hard enough */

	    k = j;
	  }
	  if(k < j)			/* wrong order ? */
	    l = k, k = j, j = l;
	  search_entries = ZRENEWP(search_entries, BuTapeEntry,
					num_search_entries + k - j + 1);
	  if(!search_entries)
	    nomemerrexit();

	  l = -1;		/* check, if we have ...@host%port */
	  if( (cptr = strstr(cartranges[i], SEPLOC)) ){
	    cptr++;
	    cptr2 = strstr(cptr, PORTSEP);
	    if(cptr2)
		*cptr2 = '\0';

	    if(!isfqhn(cptr))
		cptr = NULL;
	    else{
		cptr = strdup(cptr);
		if(!cptr)
		  nomemerrexit();

		if(cptr2)
		  sscanf(cptr2 + 1, "%d", &l);
	    }
	  }
	  for(; j <= k; num_search_entries++, j++){
	    search_entries[num_search_entries].cart = j;
	    search_entries[num_search_entries].server = cptr;
	    search_entries[num_search_entries].port = l;
	  }
	}
    }
    if(num_prev_carts){	/* evaluate args starting with - and a number */
      f = FIRST_NUM_ARG | IGNORE_NUM_ARG_TRAILERS;
      while((num_prev_carts = get_arg_num(c, v, &idx, f)) >= 0){
	f = IGNORE_NUM_ARG_TRAILERS;
	num_prev_carts_str = v[idx];

	search_entries = ZRENEWP(search_entries, BuTapeEntry,
				num_search_entries + num_prev_carts + 1);
	if(!search_entries)
	  nomemerrexit();

	l = -1;			/* check, if we have ...@host%port */
	if( (cptr = strstr(num_prev_carts_str, SEPLOC)) ){
	  cptr++;
	  cptr2 = strstr(cptr, PORTSEP);
	  if(cptr2)
	    *cptr2 = '\0';

	  if(!isfqhn(cptr))
	    cptr = NULL;
	  else{
	    cptr = strdup(cptr);

	    if(cptr2)
		sscanf(cptr2 + 1, "%d", &l);
	  }
	}

	if(find_tapepos(&actinscart, NULL, &numcarts,
						INSERT_POSITION, cptr, l)){
	  errmsg("Error: Cannot determine actual tape writing position");
	  do_exit(26);
	}

	if(num_prev_carts + 1 > numcarts){
	  errmsg("Warning: Too many previous cartridges specified, reset to %d.\n",
				(int) numcarts - 1);
	  num_prev_carts = numcarts - 1;
	}

	for(i = actinscart - num_prev_carts; i <= actinscart; i++){
	  search_entries[num_search_entries].cart = (i >= 1 ? i : (numcarts + i));
	  search_entries[num_search_entries].server = cptr;
	  search_entries[num_search_entries].cart = l;
	  num_search_entries++;
	}
      }
    }
		/* set all names, so we don't have to deal with NULL later */
    for(i = 0; i < num_search_entries; i++){
	if(!search_entries[num_search_entries].server){
	  cptr = search_entries[num_search_entries].server =
					strdup(backuphosts[serveridx]);
	  if(!cptr)
	    nomemerrexit();
	}
    }

		/* There is a special case to handle: no cartridge numbers,
		 * servers etc. are supplied (num_search_entries == 0),
		 * then we step through the known backup servers going back
		 * through the cartridges beginning with the actual insert
		 * cartridge on that server. We make a list, that only
		 * contains entries for server and port. In the loop below
		 * that special case is caught  and the loop variable is
		 * always forced to 0, what leads to infinite repetition.
		 * The cartridge entry is repeatedly updated before use.
		 * The stopidx is misused as counter for the cartridges
		 * incremented each run of the loop. The loop ends, if
		 * either all cartridges are through or it seems, enough
		 * cartridges have been read.
		 */

    if(num_search_entries > 0){
	q_sort(search_entries, num_search_entries,
				sizeof(BuTapeEntry), cmp_tapeEntry_loc);

	for(i = 0; i < num_search_entries - 1; i++){
	  if(!cmp_tapeEntry_loc(search_entries + i, search_entries + i + 1)){
	    ZFREE(search_entries[i].server);
	    memmove(search_entries + i, search_entries + i + 1,
			sizeof(BuTapeEntry) * (num_search_entries - i - 1));
	    num_search_entries--;
	    i--;
	  }
	}

	num_to_search = num_search_entries;
    }
    else{
	search_entries = NEWP(BuTapeEntry, num_backuphosts);
	if(!search_entries)
	  nomemerrexit();
	num_to_search = num_backuphosts;

	for(i = 0; i < num_backuphosts; i++){
	  search_entries[i].server = backuphosts[i];
	  search_entries[i].port = backupports[i];

	  j = find_tapepos(&search_entries[i].inscart, NULL,
				&search_entries[i].bunum, INSERT_POSITION,
				search_entries[i].server,
				search_entries[i].port);

	  if(j){
	    fprintf(stderr, "Warning: Cannot get cartridge position from"
			"server %s, port %d\n", search_entries[0].server,
					(int) search_entries[0].port);
	    break;
	  }
	}
    }

    latest_bunum_cart = earliest_bunum_cart =
			latest_bunum = earliest_bunum = 0;

    while(num_to_search > 0){		/* search through the storage units */
	stopidx = -1;
	if(num_search_entries) forever{			/* pseudo loop */
	  noservavail = 0;		/* search given triples with same */
	  startidx = ++stopidx;			/* server and port number */
	  if(startidx >= num_to_search){	/* if no server available */
	    startidx = stopidx = 0;	/* take the first one and wait ... */
	    noservavail = 1;
	  }

	  while(!cmp_tapeEntry_loc(search_entries + startidx,
						search_entries + stopidx)){
	    if(stopidx + 1 >= num_to_search)
		break;

	    stopidx++;
	  }
	  if(noservavail)
	    break;

	  i = get_streamerstate(&actstate, search_entries[startidx].server,
				search_entries[startidx].port);
	  if(i)
	    continue;

	  i = actstate & STREAMER_STATE_MASK;
	  if(i != STREAMER_READY && i != STREAMER_UNLOADED)
	    continue;

	  if(i == STREAMER_UNLOADED){	/* no cartridge inside streamer */
	    if(!(actstate & STREAMER_CHANGEABLE))	/* can we load ? */
		continue;
							/* try to load ! */
	    compose_clntcmd(tmpbuf, NULL, search_entries[startidx].server,
				search_entries[startidx].port, 0, cryptfile,
				search_entries[startidx].cart, 1, 0, 0, 0, NULL);
	    if(system(tmpbuf))
		continue;		/* loading right cartridge failed */
	  }

	  j = get_tapepos(&actcart, NULL, NULL, ACCESS_POSITION,
				search_entries[startidx].server,
				search_entries[startidx].port);
	  if(j)
	    continue;

	  j = find_tapepos(&inscart, NULL, NULL, INSERT_POSITION,
				search_entries[startidx].server,
				search_entries[startidx].port);
	  if(j)
	    continue;

		/* ok, now we know the server, port number, state,
		 * cartridge in drive and which cartridges we need
		 * to scan. Now we're going to resort the sublist
		 * we have specified by startidx and stopidx, so
		 * we can probably start with the cartridge, that
		 * is actually in the drive.
		 */

	  for(i = startidx; i <= stopidx; i++){
	    if(actcart <= search_entries[i].cart)
		break;			/* we start with the i-th */
	  }
	  if(i <= stopidx && i > startidx){	/* i is valid */
	    tmpentries = NEWP(BuTapeEntry, num_to_search);
	    if(!tmpentries)
		nomemerrexit();

	    memcpy(tmpentries + startidx, search_entries + i,
				(stopidx + 1 - i) * sizeof(BuTapeEntry));
	    memcpy(tmpentries + startidx + stopidx + 1 - i,
					search_entries + startidx,
					(i - startidx) * sizeof(BuTapeEntry));
	    free(search_entries);
	    search_entries = tmpentries;
	  }
				/* this ends our semi-pseudo-loop, cause */
	  break;		/* we have a result here and can go on */
	}			/* and now really scan the tapes */

	if(!num_search_entries)		/* if no cartridges, hosts, ports */
	  startidx = stopidx = 0;	/* etc are given, step back */
	    
	for(idx = startidx; idx <= stopidx; idx++){	/* for sublist */
	  if(!num_search_entries){	/* no cartridges ... given */
					/* stopidx is misused as counter */
	    search_entries[0].cart = search_entries[0].inscart - stopidx;

	    if(search_entries[0].cart < 1){		/* rotate back */
	      search_entries[0].cart += search_entries[0].bunum;

	      if(search_entries[0].cart == search_entries[0].inscart)
		break;		/* we are through all cartridges */
	    }

	    idx = 0;		/* set index, also forces endless loop  */
	    stopidx++;		/* increment cartridge counter */
	  }

	  compose_clntcmd(tmpbuf, "Ivnl", search_entries[idx].server,
			search_entries[idx].port, 0, cryptfile,
			search_entries[idx].cart, 1, 0, 0, 0, NULL);

	  pid = -1;				/* start scan program */
	  fd = open_from_pipe(tmpbuf, NULL, 1 + 2, &pid);
	  if(fd < 0){
	    fprintf(stderr, "Error: Cannot scan cartridge %d.\n",
						(int) search_entries[idx].cart);
	    if(pid >= 0)
		waitpid_forced(pid, &pst, 0);
	  }
	  else{
	    fp = fdopen(fd, "r");
	    if(!fp){
		errmsg("Strange Error: Cannot get filepointer for tape index");
	    }
	    else{
	      SETZERO(act_tape_entry);	/* this is our buffer for act scan */

	      while(!feof(fp)){			/* read index until eof */
		ZFREE(line);

		line = fget_alloc_str(fp);
		if(!line)
		  continue;

		SETZERO(read_tape_entry);
		i = eval_index_line(line, &read_tape_entry.server,
				&read_tape_entry.port, &read_tape_entry.cart,
				&read_tape_entry.file, NULL);
		if(INDEX_FILEENTRY(i) && !line[i]){
		  ZFREE(act_tape_entry.server);	/* have an entry like 1.2: */
		  SETZERO(act_tape_entry);
		  COPYVAL(act_tape_entry, read_tape_entry);
		  SETZERO(read_tape_entry);
		}
		else{	/* if we have header line and all entries are there */
		  if(!read_header_line(line, &act_tape_entry.mode,
				&act_tape_entry.budate, &act_tape_entry.bunum,
				&(bu_ownerhost.nodename[0]))){
		    fprintf(stdout, "%s%s%d%s%d.%d: %s",
					act_tape_entry.server, PORTSEP,
					(int) act_tape_entry.port, LOCSEP,
					(int) act_tape_entry.cart,
					(int) act_tape_entry.file, line);
		    if(!strcmp(bu_ownerhost.nodename, srchost)
					&& act_tape_entry.cart != 0
					&& act_tape_entry.file != 0
					&& act_tape_entry.budate != (time_t) 0
					&& act_tape_entry.bunum != 0){
			tape_entries = ZRENEWP(tape_entries, BuTapeEntry,
					num_tape_entries + 1);
			if(!tape_entries)
			  nomemerrexit();	/* store new entry */

			COPYVAL(tape_entries[num_tape_entries], act_tape_entry);
			num_tape_entries++;

				/* update the per cartridge bu number counters */
			if(latest_bunum_cart && earliest_bunum_cart){
			  if(act_tape_entry.bunum > latest_bunum_cart)
			    latest_bunum_cart = act_tape_entry.bunum;
			  if(act_tape_entry.bunum < earliest_bunum_cart)
			    earliest_bunum_cart = act_tape_entry.bunum;
			}
			else{
			  latest_bunum_cart = earliest_bunum_cart = act_tape_entry.bunum;
			}
				/* update the global bu number counters */

			if(latest_bunum && earliest_bunum){
			  if(latest_bunum_cart > latest_bunum)
			    latest_bunum = latest_bunum_cart;
			  if(earliest_bunum_cart < earliest_bunum)
			    earliest_bunum = earliest_bunum_cart;
			}
			else{
			  latest_bunum = earliest_bunum = act_tape_entry.bunum;
			}

			SETZERO(act_tape_entry);
		    }
		  }
		}

		ZFREE(read_tape_entry.server);
	      }
	      ZFREE(line);

	      fclose(fp);
	      waitpid_forced(pid, &pst, 0);
	      if(WEXITSTATUS(pst))
		errmsg("Warning: Program listing index had bad exit status");
	    }
	  }			/* if could open pipe from scan program */

	  if(!num_search_entries){	/* no cartridges ... given */
	    if(latest_bunum && earliest_bunum
				&& earliest_bunum_cart + 2 <= latest_bunum){
		latest_bunum_cart = earliest_bunum_cart = 0;
	 	break;	/* next server if we have at least 2 full bu's found */
	    }
	  }
	}				/* foreach cartridge sublist */

	if(!num_search_entries)
	  startidx = stopidx = 0;
					/* remove sublist from list */
	memmove(search_entries + startidx, search_entries + stopidx + 1,
		(num_to_search - (stopidx + 1)) * sizeof(BuTapeEntry));
	num_to_search -= stopidx - startidx + 1;
    }			/* while we have storage units to search */

				/* now we must sort the entries */
    q_sort(tape_entries, num_tape_entries,
				sizeof(BuTapeEntry), cmp_tapeEntry_time);

    if(!num_search_entries){			/* if no carts supplied: */
      earliest_bunum = latest_bunum - 1;	/* throw out unneeded */
      for(i = 0; i < num_tape_entries - 1; i++){
	if(tape_entries[i].bunum < earliest_bunum){
	  memmove(tape_entries + i, tape_entries + i + 1,
			(num_tape_entries - i - 1) * sizeof(BuTapeEntry));
	  num_tape_entries--;
	  i--;
	}
      }
    }		/* now we nearly have it (pooooh) */

    if(do_listing){
	fp = stdout;
	for(i = 0; i < num_tape_entries; i++){
	  fprintf(fp, EMREC_PREFIX "Backup:    %8d\n",
						(int) tape_entries[i].bunum);
	  if(tape_entries[i].server){
	    fprintf(fp, EMREC_PREFIX "Server:    %8s\n",
						tape_entries[i].server);
	    if(tape_entries[i].port > 0){
		fprintf(fp, EMREC_PREFIX "Port:    %8d\n",
						(int) tape_entries[i].port);
	    }
	  }

	  fprintf(fp, EMREC_PREFIX "Cartridge: %8d\n",
						(int) tape_entries[i].cart);
	  fprintf(fp, EMREC_PREFIX "File:      %8d\n",
						(int) tape_entries[i].file);
	}
    }

    if(dont_restore || do_listing)	/* that's it with -n or -l option */
	do_exit(0);

    servers = NEWP(UChar *, num_tape_entries);
    ports = NEWP(Int32, num_tape_entries);
    carts = NEWP(Int32, num_tape_entries);
    files = NEWP(Int32, num_tape_entries);
    if(!carts || !files || !servers || !ports)
	nomemerrexit();

    for(i = 0; i < num_tape_entries; i++){
	servers[i] = tape_entries[i].server;
	ports[i] = tape_entries[i].port;
	carts[i] = tape_entries[i].cart;
	files[i] = tape_entries[i].file;
    }
    num_backups = num_tape_entries;
    act_bunum = latest_bunum + 1;

    ZFREE(search_entries);
    ZFREE(tape_entries);
  }
  else{			/* read backup storing positions from stdin */
    if(re_compile_pattern(EMREC_PREFIX_RE CARTRIDGE_RE,
			strlen(EMREC_PREFIX_RE CARTRIDGE_RE), &cartridge_re)
	|| re_compile_pattern(EMREC_PREFIX_RE FILE_RE,
			strlen(EMREC_PREFIX_RE FILE_RE), &file_re)
	|| re_compile_pattern(EMREC_PREFIX_RE SERVER_RE,
			strlen(EMREC_PREFIX_RE SERVER_RE), &server_re)
	|| re_compile_pattern(EMREC_PREFIX_RE PORT_RE,
			strlen(EMREC_PREFIX_RE PORT_RE), &port_re)
	|| re_compile_pattern(EMREC_PREFIX_RE BACKUP_RE,
			strlen(EMREC_PREFIX_RE BACKUP_RE), &backup_re))
      nomemerrexit();

    fp = stdin;

    lines = NEWP(UChar *, 1);
    while(!feof(fp)){				/* read until eof */
      line = fget_alloc_str(fp);
      if(!line)
	continue;
      if(!strstr(line, EMREC_PREFIX))
	continue;

      lines = RENEWP(lines, UChar *, num_lines + 1);
      if(!lines)
	nomemerrexit();

      lines[num_lines] = line;
      num_lines++;

      if(re_find_match(&backup_re, line, NULL, NULL) >= 0){
        if(act_bunum != (i = trailingint(line))){
	  prev_bunum = act_bunum;
	  act_bunum = i;
	  for(i = num_lines - 2; i >= 0; i--){
	    if(re_find_match(&backup_re, lines[i], NULL, NULL) >= 0
			&&  trailingint(lines[i]) < prev_bunum){
	      i++;
	      while(re_find_match(&backup_re, lines[i], NULL, NULL) < 0)
		i++;
	      for(j = i - 1; j >= 0; j--)
		free(lines[j]);
	      num_lines -= i;
	      memmove(lines, lines + i, num_lines * sizeof(UChar *));
	      break;
	    }
	  }
	}
      }
    }

    servers = NEWP(UChar *, num_lines / 3);
    ports = NEWP(Int32, num_lines / 3);
    carts = NEWP(Int32, num_lines / 3);	/* make cartridges and files list */
    files = NEWP(Int32, num_lines / 3);
    if(!carts || !files || !ports || !servers)
      nomemerrexit();

		/* fill the cartridges and files list from the read lines */
    for(num_backups = 0, l = 0; l < num_lines;){
      servers[num_backups] = NULL;
      ports[num_backups] = -1;

      if(re_find_match(&backup_re, lines[l], NULL, NULL) < 0){
	l++;
	continue;
      }

      while(re_find_match(&cartridge_re, lines[l], NULL, NULL) < 0
							&& l < num_lines){
	if(re_find_match(&server_re, lines[l], NULL, NULL) >= 0){
	  servers[num_backups] = strword(lines[l], -1);
	}
	else if(re_find_match(&port_re, lines[l], NULL, NULL) >= 0){
	  ports[num_backups] = trailingint(lines[l]);
	}

	l++;
      }

      if(l >= num_lines)
	break;

      carts[num_backups] = trailingint(lines[l]);

      while(re_find_match(&file_re, lines[l], NULL, NULL) < 0
							&& l < num_lines)
	l++;
      if(l >= num_lines)
	break;

      files[num_backups] = trailingint(lines[l]);

      num_backups++;
    }
  }

  forever{			/* create and check index file name */
    sprintf(tmpbuf, "%d", act_bunum);
    indexfile = strapp(indexfilepart, tmpbuf);
    zindexfile = strapp(indexfile, COMPRESS_SUFFIX);

    if(!stat(indexfile, &statb) || !stat(zindexfile, &statb)){
	fprintf(stdout,
		"Warning: Filename logfile for backup number %d exists,"
		" trying next.\n",
			act_bunum);

    act_bunum++;
    free(zindexfile);
    free(indexfile);
    continue;
  }
  else
    break;
  }

  opid = -1;
  ofd = -1;
  if(compresslogfiles){
    ofd = open_to_pipe(compresscmd, zindexfile, 1, &opid, 0600);
  }
  if(ofd < 0){
    ofd = open_to_pipe(NULL, indexfile, 1, &i, 0600);
    if(ofd < 0){
	fprintf(stderr, "Error: Cannot open filename logfile.\n");
    }
  }

  for(i = 0; i < num_backups; i++){
    if(!restore_emlist){
      fprintf(stdout,
		"Going to restore files from server %s, port %d, cartridge %d, file %d.\n",
				servers[i] ? servers[i] : backuphosts[0],
				(int) ports[i], (int) carts[i], (int) files[i]);

      compose_clntcmd(tmpbuf, "xvnlau", servers[i], ports[i], 0, cryptfile,
		carts[i], files[i], time_older, time_newer, 0, restoreroot);
    }
    else{
      fprintf(stdout,
		"Going to restore filenames from server %s, port %d, cartridge %d, file %d.\n",
				servers[i] ? servers[i] : backuphosts[0],
				(int) ports[i], (int) carts[i], (int) files[i]);

      compose_clntcmd(tmpbuf, "tnl", servers[i], ports[i], 0, cryptfile,
			carts[i], files[i], time_older, time_newer, 0, NULL);
    }

    pid = -1;
    fd = open_from_pipe(tmpbuf, NULL, 1 + 2, &pid);
    if(fd < 0){
      fprintf(stderr, "Error: Cannot restore files.\n");
      if(pid >= 0)
	waitpid_forced(pid, &pst, 0);

      continue;
    }
    j = 0;
    while((l = read(fd, tmpbuf, 50)) > 0){
      if(ofd >= 0){
	if(write(ofd, tmpbuf, l) < l){
	  j++;
	  if(j < 10)
	    errmsg("Warning: Writing to filename logfile failed");
	  if(j == 9)
	    errmsg("Further warnings suppressed");
	}
      }
      write(1, tmpbuf, l);
    }
    close(fd);
    waitpid_forced(pid, &pst, 0);

    if(WEXITSTATUS(pst)){
      errmsg("Warning: Something went wrong during restore. See previous output for details");
    }
  }

  if(ofd >= 0)
    close(ofd);
  if(opid >= 0){
    waitpid_forced(opid, &pst, 0);
    if(WEXITSTATUS(pst)){
	fprintf(stderr, "Warning: Compressing \"%s\" returned bad status.\n",
		indexfile);
    }
  }

  if(write_uns_file(numfile, act_bunum)){
    fprintf(stderr, "Warning: Cannot write file \"%s\".\n", numfile);
  }

  do_exit(0);
}
