/*
 * 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>
#include <pwd.h>
#ifdef	HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include <unistd.h>
#include <stdarg.h>
#include <mvals.h>
#include <fileutil.h>
#include <sysutils.h>
#include <netutils.h>
#include <utils.h>
#include <x_regex.h>
#include <packer.h>

#include "backup.h"
#include "server.h"

#define	CLEANUP		{ goto cleanup; }
#define	GETOUT		{ goto getout; }
#define	CLEANUPR(rval)	{ r = rval; goto cleanup; }
#define	GETOUTR(rval)	{ r = rval; goto getout; }

#define	ES		((UChar *) "")

#define	WS		"[ \t]*"

/* The following hurts me, but there is no way out */
static char	*BACKUP_RE = TN_("[ \t]*" "[Bb][Aa][Cc][Kk][Uu][Pp]");
static char	*CARTRIDGE_RE = TN_("[ \t]*" "[Cc][Aa][Rr][Tt][Rr][Ii][Dd][Gg][Ee]");
static char	*FILE_RE = TN_("[ \t]*" "[Ff][Ii][Ll][Ee]");
static char	*SERVER_RE = TN_("[ \t]*" "[Ss][Ee][Rr][Vv][Ee][Rr]");
static char	*PORT_RE = TN_("[ \t]*" "[Pp][Oo][Rr][Tt]");
static char	*NCARTS_RE = TN_("[Nn][Uu][Mm]\\([Bb][Ee][Rr]\\)?" "[ \t]*" "[Oo][Ff]" \
			"[ \t]*" "[Cc][Aa][Rr][Tt]\\([Rr][Ii][Dd][Gg][Ee]\\)?[Ss]");

#define	EMREC_PREFIX	"@@@===--->>>"
#define	EMREC_POSTFIX	"<<<---===@@@"
#define	EMREC_PREFIX_RE	"^" WS EMREC_PREFIX

#define	EMREC_TAPE_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_RESTORE		2
#define	MODE_VERIFYBU		3
#define	MODE_COPY_TAPE		4
#define	MODE_UPDATE_INDEXES	5

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

#define	FIRST_NUM_ARG		1
#define	IGNORE_NUM_ARG_TRAILERS	2

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

#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)

typedef	struct _scandir_args_ {
  int	fd;
  Flag	entry_found;
  Flag	write_failed;
  Flag	have_location;
  int	ctofd;
  int	cfromfd;
  int	cerrfd;
  UChar	*read_data;
  Int32	read_data_nall;
  Int32	num_read_data;
  UChar	*read_err;
  Int32	read_err_nall;
  Int32	num_read_err;
}	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;

typedef struct _datalocator_ {
  Int32		cart;
  Int32		file;
  Int32		efile;
  UChar		*server;
  Int32		port;
} DataLocator;

typedef struct streamerstat {
  UChar		devstatus;
  Int32		serverstatus;
  Int32		numactive;
  Int32		numpending;
  UChar		serverid[256];
  Uns32Range	*needed_tapes;
  Uns32Range	*deleted_tapes;
}	StreamerStat;

typedef struct serveridtab {
  UChar		*servername;
  Int32		serverport;
  UChar		*serverid;
} ServerIdTab;

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

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	*confdir = NULL;
UChar   *logdir = NULL;

ReplSpec	dir_pat_repl[] = {
	{ "%B",	NULL, &bindir },
	{ "%L",	NULL, &libdir },
	{ "%V",	NULL, &vardir },
	{ "%C",	NULL, &confdir },
	{ "%I",	NULL, &logdir },
	};

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	*save_entry_file = NULL;
UChar	**save_entries = NULL;
UChar	*level_times_file = NULL;
KeyValuePair	*level_timespecs = NULL;
Int32	num_level_timespecs = 0;
UChar	*needed_tapes_file = NULL;
UChar	**needed_tapes_lines = NULL;
Int32	num_needed_tapes_lines = 0;
UChar	*server_ids_file = NULL;
UChar	*index_ages_file = NULL;
UChar	*start_pos_file = NULL;
UChar	*tmp_rm_on_bu_file = NULL;

UChar	*clientprogram = NULL;

UChar	*piperprogram = NULL;
UChar	*zprogram = NULL;

UChar	*backuphostlist = NULL;
UChar	**backuphosts = NULL;
Int32	num_backuphosts = 0;
UChar	*backupportlist = NULL;
UChar	**backupportstrs;
Int32	*backupports = NULL;
Int32	num_backupports = 0;
Int32	serveridx = 0;
UChar	server_id[512];		/* only needed for backup */

UChar	*rootdir = NULL;
UChar	*dirstobackupraw = NULL;
UChar	**dirstobackup = NULL;
UChar	*alldirstobackupraw = NULL;
UChar	**alldirstobackup = NULL;
UChar	*filestoskipraw = NULL;
UChar	**filestoskip = NULL;
UChar	*dirstoskipraw = NULL;
UChar	**dirstoskip = NULL;
UChar	*indexfilepart = NULL;
Int32	numindexestostore = -1;
Real32	daystostoreindexes = -1.0;
Int32	numindexestoscan = -1;
Real32	daystoscanindexes = -1.0;
UChar	*compresscmd = NULL;
UChar	*uncompresscmd = NULL;
UChar	*idxcompresscmd = NULL;
UChar	*idxuncompresscmd = NULL;
Int32	builtin_compr_level = 0;
Flag	compressbu = YES;
UChar	*logfile = NULL;
Int32	numparts = 1;
UChar	*startinfoprog = NULL;
UChar	*initprog = NULL;
UChar	*exitprog = NULL;
UChar	*reportfile = NULL;
UChar	*filelist = NULL;
Flag	compresslogfiles = YES;
UChar	**filecontentstobu = NULL;
UChar	**dont_compress = NULL;
Int32	num_dont_compress = 0;
UChar	*dont_compress_str = NULL;
UChar	*exclude_filename = NULL;
Int32	backup_level = MAXINT;

time_t	start_time;
time_t	cmp_time;

Flag	no_default_backup = NO;

UChar	*minrestoreinfo = NULL;

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

Flag	interrupted = NO;
Flag	logf_lost = NO;
UChar	*cartsetlist = NULL;
Int32	*cartsets = NULL;
Int32	num_cartsets = 0;
Flag	detach = NO;
Flag	chksum = NO;
Flag	chk_perms = NO;

Flag	verbose = NO;
Flag	quiet = NO;

sigset_t	idxproc_blk_sigs;

/* for restore */
Flag	do_counting = NO;
Flag	do_listing = NO;
Flag	do_raw_listing = NO;
Flag	ign_case = NO;
UChar	*restoreroot = NULL;
Flag	restore_all = NO;
#define	vfy_list_all	restore_all
Int32	restore_em = 0;
Flag	restore_emlist = NO;
Int32	restore_EM = 0;
Flag	preserve_existing = NO;
UChar	*beforestr = NULL;
UChar	*afterstr = NULL;
time_t	time_older = 0;
time_t	time_newer = 0;
UChar	*restoretapestr = NULL;
Uns32Range	*restoretapes = NULL;
time_t	current_bu_time = 0;
time_t	prev_bu_time = 0;
Flag	have_timerange = NO;
Flag	have_mtime_idx = NO;
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;
UChar	*formatstr = NULL;
ReplSpec	*fmt_repls = NULL;
Int32	num_fmt_repls = 0;
#define	FMT_REPL_CHARS	"nbhpcfoOmMtT"
uid_t	curuid;
gid_t	curgid;
gid_t	*curgids;
int	curngids;
UChar	*systemname = NULL;
UChar	*identity = NULL;
Flag	identity_from_arg = NO;
Flag	vardir_from_arg = NO;
UName	systeminfo;

/* for copy_tape */
UChar	*tmpdir = 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	4096
#else
#define	TMPBUFSIZ	(MAXPATHLEN * 4 + 100)
#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	},
	{ &backupportlist, NULL,
	(UChar *) "^[ \t]*[Bb]ackup[ \t_-]*[Pp]orts?:?[ \t]*",
		TypeUCharPTR	},
	{ &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	},
	{ &daystostoreindexes, NULL,
	(UChar *) "^[ \t]*[Dd]ays[-_ \t]*[Tt]o[ \t_-]*[Ss]tore[-_ \t]*[Ii]nd\\(ic\\|ex\\)es:?",
		TypeReal32	},
	{ &numindexestoscan, NULL,
	(UChar *) "^[ \t]*[Nn]um[-_ \t]*[Ii]nd\\(ic\\|ex\\)es[-_ \t]*[Tt]o[ \t_-]*[Ss]can:?",
		TypeInt32	},
	{ &daystoscanindexes, NULL,
	(UChar *) "^[ \t]*[Dd]ays[-_ \t]*[Tt]o[ \t_-]*[Ss]can[-_ \t]*[Ii]nd\\(ic\\|ex\\)es:?",
		TypeReal32	},
	{ &compresscmd, NULL,
	(UChar *) "^[ \t]*\\([Pp]rocess\\|[Cc]ompress\\)[-_ \t]*[Cc]o?m?ma?n?d:?[ \t]*",
		TypeUCharPTR	},
	{ &uncompresscmd, NULL,
	(UChar *) "^[ \t]*\\([Uu]nprocess\\|[Uu]ncompress\\)[-_ \t]*[Cc]o?m?ma?n?d:?[ \t]*",
		TypeUCharPTR	},
	{ &idxcompresscmd, NULL,
	(UChar *) "^[ \t]*[Ii]ndex[-_ \t]*\\([Pp]rocess\\|[Cc]ompress\\)[-_ \t]*[Cc]o?m?ma?n?d:?[ \t]*",
		TypeUCharPTR	},
	{ &idxuncompresscmd, NULL,
	(UChar *) "^[ \t]*[Ii]ndex[-_ \t]*\\([Uu]nprocess\\|[Uu]ncompress\\)[-_ \t]*[Cc]o?m?ma?n?d:?[ \t]*",
		TypeUCharPTR	},
	{ &builtin_compr_level, NULL,
	(UChar *) "^[ \t]*[Bb]uilt[-_ \t]*[iI]n[-_ \t]*[Cc]ompre?s?s?i?o?n?[-_ \t]*\\([Ll]evel\\)?:?",
		TypeInt32	},
	{ &dont_compress_str, NULL,
	(UChar *) "^[ \t]*[Dd]o[-_ \t]*[Nn][\'o]?t[-_ \t]*\\([Pp]rocess\\|[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]*\\([Pp]rocess\\|[Cc]ompress\\)[-_ \t]*\\([Ll]ogg?i?n?g?[-_ \t]*[Ff]iles?\\|[Ii]n?d\\(e?x\\|i?c\\)e?s?\\([Ff]iles?\\)?\\)[-_ \t]*:?",
		TypeFlag	},
	{ &compressbu, NULL,
	(UChar *) "^[ \t]*\\([Pp]rocess\\|[Cc]ompress\\)[-_ \t]*[Bb]ackupe?d?[-_ \t]*\\([Ff]iles\\)?:?",
		TypeFlag	},
	{ &cryptfile, NULL,
	(UChar *) "^[ \t]*\\([Ee]n\\)?[Cc]rypti?o?n?[ \t_-]*[Kk]ey[ \t_-]*[Ff]ile:?[ \t]*",
		TypeUCharPTR	},
	{ &lockfile, NULL,
	(UChar *) "^[ \t]*[Ll]ocki?n?g?[-_ \t]*[Ff]ile:?[ \t]*",
		TypeUCharPTR	},
	{ &vardir, NULL,
	(UChar *) "^[ \t]*[Vv][Aa][Rr][-_ \t]*[Dd]ire?c?t?o?r?y?:?[ \t]*",
		TypeUCharPTR	},
	{ &identity, NULL,
	(UChar *) "^[ \t]*\\([Cc]lient[-_ \t]*\\)?[Ii]denti\\(ty\\|fier\\):?",
		TypeUCharPTR	},
	{ &chksum, NULL,
	(UChar *) "^[ \t]*\\([Ww]rite\\)?[Cc]heck[-_ \t]*[Ss]um\\(s\\|ming\\)?:?",
		TypeFlag	},
	{ &chk_perms, NULL,
	(UChar *) "^[ \t]*[Cc]heck[-_ \t]*[Rr]estore[-_ \t]*[Aa]ccess[-_ \t]*[Pp]ermi?s?s?i?o?n?s?:?",
		TypeFlag	},
};

#define	EM__(nmem)	{ if(!(nmem)) nomemerrexit(); }
#define	EEM__(nmem)	{ if(nmem) nomemerrexit(); }
#define	ER__(cmd, lerrfl)	{ if( (lerrfl = cmd) ) return(lerrfl); }

#define	repl_dirs(string)	repl_substrings((string), dir_pat_repl,	\
		sizeof(dir_pat_repl) / sizeof(dir_pat_repl[0]))
#define	one_more_for_restore	(restore_EM > 1 || restore_em > 1)


static void	verifybu(int, char **);
static void	restore(int, char **);
static void	restoreall(int, char **);
static void	restoreem(int, char **);
static void	copy_tape(int, char **);
static Int32	get_args(int, char **);
static Int32	get_restore_args(int, char **);
static Int32	get_verify_args(int, char **);
static Int32	get_copytape_args(int, char **);
static Int32	get_update_indexes_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 **, UChar **, Int32 *);
static UChar	*repl_substring_safe(UChar *, UChar *, UChar *);
static Int32	get_tapepos(Int32 *, Int32 *, Int32 *, Int8, UChar *, Int32, UChar *);
static Int32	get_streamerstate(StreamerStat *, UChar *, Int32);
static Int32	read_header_line(UChar *, Int32 *, time_t *, Int32 *,
						UChar *, UChar *);
static Int32	eval_index_line(UChar *, UChar **, Int32 *, Int32 *, Int32 *,
				int *, time_t *);
static void	compose_clntcmd(UChar *, UChar *, UChar *, UChar *, Int32,
		Int32, UChar *, Int32, Int32, time_t, time_t, Int32, UChar *);
static Int32	add_server_id_entries(UChar *, ServerIdTab *, Int32);
static ServerIdTab	*get_server_id_entries(UChar *, Int32 *);
static Int32	serverid_from_netaddr(ServerIdTab *, UChar *, Int32, UChar *);
static Uns32Range	*get_needed_tapes(UChar *, UChar *);
static Int32	remove_needed_tapes(UChar *, UChar *, Uns32Range *);
static Int32	remove_tapes_from_idxes(StreamerStat *, Int32, UChar *, Int32);
static UChar	*repl_fmts(UChar *, ReplSpec *, Int32, UChar *, time_t);
static int	open_idxfile_read(Int32, int *, UChar **, Flag);
static void
do_exit(int s)
{
  int	fd, i;

  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 && (mode == MODE_FULL_BACKUP || mode == MODE_INCR_BACKUP)){
    sprintf(tmpbuf, "%d", s);

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

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

    system(exitprog);

  }

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

  for(i = 0; i < 10; i++){
    if(tmpfiles[i]){
	unlink(tmpfiles[i]);
	free(tmpfiles[i]);
    }
  }

  exit(s);
}

static void
errmsg(UChar * msg, ...)
{
  FILE		*lfp = NULL;
  UChar		lc;
  Int32		lm;
  va_list	args;

  va_start(args, msg);

  if(logfile)
    if(strcmp(logfile, "-"))
	lfp = fopen(logfile, "a");

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

  lm = strlen(msg);
  lc = (lm > 0 ? msg[lm - 1] : '\0');

  vfprintf(stderr, msg, args);
  fprintf(stderr, "%s\n", lc == '.' ? "" : ".");

  va_end(args);
}

static void
logmsg(UChar * msg, ...)
{
  FILE		*lfp = NULL;
  Flag		si = NO;
  Int32		lm;
  UChar		lc;
  va_list	args;

  va_start(args, msg);

  if(logfile){
    if(!strcmp(logfile, "-")){
	lfp = stdin;
	si = YES;
    }
    else
	lfp = fopen(logfile, "a");
  }

  lm = strlen(msg);
  lc = (lm > 0 ? msg[lm - 1] : '\0');

  if(lfp){
    fprintf(lfp, "%s, ", actimestr());
    vfprintf(lfp, msg, args);
    fprintf(lfp, "%s\n", lc == '.' ? "" : ".");
    if(!si)
	fclose(lfp);
  }

  if(verbose){
    vfprintf(stdout, msg, args);
    fprintf(stdout, "%s\n", lc == '.' ? "" : ".");
  }

  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;

  EM__(newstr = repl_substring(org, to_subst, subst_by));

  return(newstr);
}

Int32
replace_if_pipe(UChar ** str, Int32 mode, Int32 level, Int32 part)
{
  FILE		*fp;
  UChar		*newstr = NULL, *line = NULL, *sep, *newcmd = NULL;
  UChar		typestr[30];
  Int32		r = 0;

  if(strncmp(*str, "| ", 2))
    return(0);

  typestr[0] = (mode == MODE_FULL_BACKUP ? 'F' :
		(level > 0 ? 'L' : 'I'));
  typestr[1] = '\0';
  if(mode == MODE_INCR_BACKUP && level > 0)
    sprintf(typestr + 1, "%d", (int) level);
  if(mode == MODE_FULL_BACKUP && part > 0)
    sprintf(typestr + 1, "%d", (int) part);

  newcmd = repl_substring_safe(*str + 2, "%T", typestr);

  fp = popen(newcmd, "r");
  if(!fp){
    errmsg(T_("Cannot run command `%s'"), *str + 2);
    CLEANUPR(-1);
  }

  sep = "";
  newstr = strdup("");
  while(!feof(fp)){
    ZFREE(line);
    line = fget_alloc_str(fp);
    if(!line)
	continue;
    while(chop(line));

    newstr = strchain(newstr, sep, line, NULL);
    sep = " ";
  }

  ZFREE(*str);
  *str = newstr;
  newstr = NULL;

 cleanup:
  if(fp)
    pclose(fp);
  ZFREE(newcmd);
  ZFREE(newstr);
  ZFREE(line);
  return(r);
}

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

  switch(mode){
   case MODE_RESTORE:
    if(curuid)
      fprintf(stderr,
        T_("Usage: %s [ -nlvmi ] [ -<past-backup-no> ] [ -C <root-directory> ] \\\n"
        "               [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
        "               [ -A \"<after-date>\" ] [ -B \"<before-date>\" ] \\\n"
	"               [ -k <encryption-key-file> ] [ -T <tapes> ] \\\n"
	"               [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"               [ -Z <built-in-compress-level> ] [ -F <format> ] \\\n"
	"               [ { -N <num-indexfiles> | -O <indexfile-age-days> } ] \\\n"
        "               [ -p ] <path-pattern> [ [ -p ] <path-patterns> [ ... ] ]\n"),
		pname);
    else
      fprintf(stderr,
        T_("Usage: %s [ -nlvmi ] [ -<past-backup-no> ] [ -C <root-directory> ] \\\n"
        "               [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
	"               [ -c <configuration-file> ] [ -W <identity> ] \\\n"
	"               [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
        "               [ -A \"<after-date>\" ] [ -B \"<before-date>\" ] \\\n"
	"               [ -k <encryption-key-file> ] [ -T <tapes> ] \\\n"
	"               [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"               [ -Z <built-in-compress-level> ] [ -F <format> ] \\\n"
	"               [ -N <num-indexfiles> | -O <indexfile-age-days> ] \\\n"
        "               [ -p ] <path-pattern> [ [ -p ] <path-patterns> [ ... ] ]\n"
        "       %s -a [ -nlvm ] [ -<past-backup-no> ] [ -C <root-directory> ] \\\n"
        "               [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
	"               [ -c <configuration-file> ] [ -W <identity> ] \\\n"
	"               [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
	"               [ -k <encryption-key-file> ] \\\n"
	"               [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"               [ -Z <built-in-compress-level> ] [ -F <format> ]\n"
	"       %s -{ef} [ -evm ] [ -C <root-directory> ] [ -h <backuphosts> ] \\\n"
       	"               [ -P <backup-ports> ] [ -V <var-directory> ] \\\n"
	"               [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"               [ -Z <built-in-compress-level> ] \\\n"
	"               [ -k <encryption-key-file> ] [ -W <identity> ] \\\n"
	"               [ -c <configuration-file> ] < <startup-info-file>\n"
	"       %s -E [ -Enlvm ] [ -C <root-directory> ] [ -h <backuphosts> ] \\\n"
       	"               [ -P <backup-ports> ] [ -V <var-directory> ] \\\n"
	"               [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"               [ -Z <built-in-compress-level> ] \\\n"
	"               [ -k <encryption-key-file> ] [ -W <identity> ] \\\n"
	"               [ -c <configuration-file> ] \\\n"
	"               [ <cartridge-number> | <cartridge-range> ] ... ]\n"),
                        pname, pname, pname, pname);
    break;

   case MODE_UPDATE_INDEXES:
    if(curuid)
      fprintf(stderr, "%s: %s\n", T_("Usage"), pname);
    else
      fprintf(stderr,
	T_("Usage: %s [ -v ] [ -c <configuration-file> ] \\\n"
	"               [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
	"               [ -I <indexfile-part> ] [ -V <var-directory> ] \\\n"
	"               [ -k <encryption-key-file> ] [ -W <identity> ] \\\n"
	"               [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"               [ -Z <built-in-compress-level> ] \\\n"),
	pname);
    break;

   case MODE_VERIFYBU:
    fprintf(stderr,
	T_("Usage: %s [ -lav ] [ -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> ] [ -W <identity> ] \\\n"
	"              [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"              [ -Z <built-in-compress-level> ]\n"),
	pname);
    break;

   case MODE_COPY_TAPE:
    fprintf(stderr,
	T_("Usage: %s [ -v ] [ -c <configuration-file> ] \\\n"
	"                   [ -l <logfile> ] [ -T <tmpdir> ] \\\n"
	"                   [ -h <source-server> ] [ -P <source-serverport> ] \\\n"
	"                   [ -C <source-cartridge> ] [ -F <tape-filenumber> ] \\\n"
	"                   [ -k <source-encryption-key-file> ] \\\n"
	"                   [ -D [ -h <target-server> ] [ -P <target-serverport> ] \\\n"
	"                     [ -C <target-cartridge> ] [ -k <target-encryption-key-file> ] ]\n"),
	pname);
    break;

   default:
    fprintf(stderr,
	T_("Usage: %s [ -daG ] [ {+-}LBx ] [ <files> <directories> ... ] \\\n"
	"                   [ -C <root-directory> ] [ -F \"<files-to-skip>\" ] \\\n"
	"                   [ -D \"<directories-to-skip>\" ] \\\n"
	"                   [ -c <configuration-file> ] [ -W <identity> ] \\\n"
	"                   [ -h <backuphosts> ] [ -P <backup-ports> ] \\\n"
	"                   [ -I <indexfile-part> ] [ -Q <backup-level> ] \\\n"
	"                   [ { -N <num-indexes-to-store> ] | \\\n"
	"                     -O <max-age-of-indexes-to-store-in-days> } ] \\\n"
	"                   [ -z <process-cmd> <unprocess-cmd> ] \\\n"
	"                   [ -Z <built-in-compress-level> ] \\\n"
	"                   [ -s \"<dont-process-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(interrupted){
    fprintf(stderr, T_("Repeated interruption, please stand by during cleanup.\n"));

    if(s == SIGSEGV || s == SIGBUS)
	exit(99);

    return;
  }
    
  if(mode != MODE_INCR_BACKUP && mode != MODE_FULL_BACKUP){
    kill(getpid(), s);
    do_exit(2);
  }

  if(s != SIGPIPE){
    signal(s, sig_handler);

    if(interrupted)
	return;

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

    logf_lost = YES;
  }

  interrupted = YES;
}

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(logfile){
    switch(mode){
     case MODE_FULL_BACKUP:
      if(numparts > 1)
	logmsg(T_("Full backup part %d interrupted"), part);
      else
	logmsg(T_("Full backup interrupted"));
      break;

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

     default:
      break;
    }
  }

  do_exit(2);
}

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

  if(logfile){
    if(mode == MODE_FULL_BACKUP){
      if(numparts > 1)
	errmsg(T_("Full backup part %d failed"), part);
      else
	errmsg(T_("Full backup failed"));
    }
    if(mode == MODE_INCR_BACKUP){
      errmsg(T_("Incremental backup failed"));
    }
  }

  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(T_("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(T_("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(T_("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_pint_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
handle_client_output(ScanDirArgs * arg, Flag * got_output)
{
  Int32		i, n;
  UChar		*cptr;

  if(got_output)
    *got_output = NO;

  while((i = read(arg->cfromfd, arg->read_data, arg->read_data_nall)) > 0){
    n = write_forced(arg->fd, arg->read_data, i);
    if(n < i)
	return(-1);

    if(got_output)
	*got_output = YES;
  }

  forever{
    if(arg->num_read_err + 1024 > arg->read_err_nall){
	arg->read_err = RENEWP(arg->read_err, UChar, arg->read_err_nall + 1024);
	EM__(arg->read_err);
	arg->read_err_nall += 1024;
    }

    i = read(arg->cerrfd, arg->read_err + arg->num_read_err, 1023);
    if(i > 0){
	arg->num_read_err += i;
	if(got_output)
	  *got_output = YES;
    }
    else
	break;

    arg->read_err[arg->num_read_err] = '\0';

    while( (cptr = strchr(arg->read_err, '\n')) ){
	*(cptr++) = '\0';
	for(i = 2; cptr - i >= arg->read_err && *(cptr - i) == '.'; i++)
	  *(cptr - i) = '\0';
	errmsg("%s", arg->read_err);
	n = (arg->read_err + arg->num_read_err) - cptr;
	memmove(arg->read_err, cptr, n * sizeof(UChar));
	arg->num_read_err = n;
    }
  }

  return(0);
}

static Int32
write_filename(UChar * name, void * data, struct stat * statbp)
{
  int		maxfd;
  UChar		*cptr = NULL;
  Int32		ret = 0, needed_size, n, i, j, nfd;
  Flag		allocated = NO, got_output;
  ScanDirArgs	*scandirargs;
  fd_set	ifds, ofds;

  scandirargs = (ScanDirArgs *) data;

  if(name){
    needed_size = strlen(name) * 4 + 5;
    if(needed_size > TMPBUFSIZ){
      cptr = NEWP(UChar, needed_size);
      if(!cptr){
	ret = errno;
	CLEANUP;
      }

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

    mk_esc_seq(name, ESCAPE_CHARACTER, cptr);
    strcat(cptr, "\n");
    name = cptr;

    n = strlen(name);
  }
  else{
    n = 1;
  }

  maxfd = MAX(MAX(scandirargs->ctofd, scandirargs->cfromfd), scandirargs->cerrfd);

  for(i = 0; i < n;){
    if(interrupted)
	break;

    FD_ZERO(&ifds);
    FD_ZERO(&ofds);
    FD_SET(scandirargs->ctofd, &ofds);
    FD_SET(scandirargs->cfromfd, &ifds);
    FD_SET(scandirargs->cerrfd, &ifds);

    got_output = NO;
    nfd = select(maxfd, &ifds, name ? (&ofds) : NULL, NULL, NULL);
    if(nfd > 0){
	if(name){
	  if(FD_ISSET(scandirargs->ctofd, &ofds)){
	    j = write(scandirargs->ctofd, name + i, n - i);
	    if(j > 0)
		i += j;
	  }
	}

	if(FD_ISSET(scandirargs->cfromfd, &ifds) ||
			FD_ISSET(scandirargs->cerrfd, &ifds)){
	  j = handle_client_output(scandirargs, &got_output);
	  if(j)
	    return(j);
	}
    }

    if(!name && (nfd < 1 || !got_output))
	break;
  }

#if 0
  if(i < n){
    errmsg(T_("Error: Could not send filename to afclient process"));

    scandirargs->write_failed = YES;
    ret = errno;
  }
#endif

  if(name)
    scandirargs->entry_found = YES;

 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);
}

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

static Int32
eval_index_line(
  UChar		*line,
  UChar		**server,
  Int32		*port,
  Int32		*cart,
  Int32		*fileno,
  int		*uid,
  time_t	*mtime)
{
  UChar		*cptr, *orgline, *newmem, *colon = NULL, a;
  int		p = 0, c = 0, f = 0, u = - MAXINT, servlen = 0, i, n;
  time_t	m = (time_t) -1;

  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;
  }

  i = sscanf(line, "%d.%d%n", &c, &f, &n);
  if(i < 2)
    return(-11);

  if(!strncmp(line + n, UIDSEP, strlen(UIDSEP))){
    i = sscanf(line, "%d.%d" UIDSEP "%d%n", &c, &f, &u, &n);
    if(i < 3)
	return(-12);
    if(line[n] != ':' && strncmp(line + n, MTIMESEP, strlen(MTIMESEP)))
	return(-13);
  }
  else if(line[n] != ':')
    return(-14);

  if(!strncmp(line + n, MTIMESEP, i = strlen(MTIMESEP)))
    m = strint2time(line + n + i);

  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 != - MAXINT && uid)
    *uid = u;
  if(c && cart)
    *cart = c;
  if(f && fileno)
    *fileno = f;
  if(m != (time_t) -1 && mtime)
    *mtime = m;

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

main(int argc, char ** argv)
{
/* uninitialized ok: uncompcmd, j, ifd, infile, outfile, inunzip,
                     outzip unzipfile, unzipfilecmd */
  UChar		*backuphome, *programfile;
  UChar		*cptr, *cptr2, **cpptr, **cpptr2, **cpptr3;
  UChar		*zippedidxf, *tmpidxf;
  UChar		*tmperrf, ***cppptr, *uncompcmd;
  UChar		*dirsarr[2], *crucial_tapes_str = NULL, *header;
  UChar		kbuf[64], vbuf[128];
  Flag		must_read_fnamlog = NO, remove_lower_levels, one_server;
  Flag		remove_previous_equal_level, errflp, success = YES;
  UChar		*filename, **dirlists[10], *tmpstartposfile;
  Int32		i, j, n, lck, num_inlines = 0, num_outlines = 0;
  Int32		startcart, startfile, last_idx_num = 0, used_port;
  int		pos, p;
  FILE		*fp;
  int		pid, pst, ppi[3], ppo[3], ppe[3], lfd, fd;
  int		ifd, opid, ipid, i1, fdflags, ofdflags, efdflags, eofdflags;
  FindParams	findparams;
  UChar		*infile, *outfile, inunzip, outzip;
  UChar		*unzipfile, **unzipfilecmd;
  UChar		*rename_from = NULL, *rename_to = NULL, **lines;
  Int32		effective_backup_level, num_server_ids, num_servers;
  Uns32Range	*new_tape_ranges, *all_tape_ranges;
  struct stat	statb, statb2;
  ServerIdTab	*server_id_tab = NULL;
  StreamerStat	*stst;

#ifdef ENABLE_NLS
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
#endif
  
  SETZERO(dirlists);
  SETZERO(tmpfiles);
  strcpy(server_id, "");

  curuid = getuid();
  curgid = getgid();
  curngids = 0;
  curgids = NULL;
  get_groups(&curngids, &curgids);

  programfile = find_program(argv[0]);
  if(!programfile){
    errmsg(T_("Error: Cannot find program file of \"%s\""), argv[0]);
    exit(4);
  }

  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, T_("Error: Command name %s not recognized.\n"), cptr);
    do_exit(1);
  }

  i = stat(programfile, &statb);
  if(i){
    errmsg(T_("Error: Cannot stat %s"), programfile);
    exit(41);
  }

  if((statb.st_mode & (S_ISUID | S_ISGID))
	&& mode != MODE_RESTORE && mode != MODE_UPDATE_INDEXES){
    errmsg(T_("Error: %s is installed SUID or SGID. Please check installation"),
			programfile);
    exit(42);
  }

  /* check usage */
  get_args(argc, argv);

  systemname = get_my_off_hn();
  if(!systemname){
    i = uname(&systeminfo);
    if(i < 0){
	fprintf(stderr, T_("Warning: Cannot determine hostname.\n"));
	systemname = "???";
    }
    else
	systemname = systeminfo.nodename;
  }
/*  cptr = strchr(systemname, '.');
  if(cptr)
    *cptr = '\0';
*/ /* not needed anymore, have same_host() function now */

  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);

  /* signals blocked during execution of index processing processes */
  sigemptyset(&idxproc_blk_sigs);
  sigaddset(&idxproc_blk_sigs, SIGINT);	/* this is advanced from Ctrl-C */

  /* 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(T_(CARTRIDGE_RE), strlen(T_(CARTRIDGE_RE)), &cartridge_re)
	|| re_compile_pattern(T_(FILE_RE), strlen(T_(FILE_RE)), &file_re)
	|| re_compile_pattern(T_(SERVER_RE), strlen(T_(SERVER_RE)), &server_re)
	|| re_compile_pattern(T_(PORT_RE), strlen(T_(PORT_RE)), &port_re)
	|| re_compile_pattern(T_(NCARTS_RE), strlen(T_(NCARTS_RE)), &ncarts_re)
	|| re_compile_pattern(T_(BACKUP_RE), strlen(T_(BACKUP_RE)), &backup_re))
    nomemerrexit();

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

#ifdef	ORIG_DEFAULTS

  if(!backuphome){
    backuphome = programfile;
    EM__(cptr = mkabspath(backuphome, NULL));
    free(backuphome);
    backuphome = resolve_path__(cptr, NULL);
    if(!backuphome){
	errmsg(T_("Error: Cannot follow path \"%s\""), cptr);
	do_exit(29);
    }
    free(cptr);

    cptr = FN_LASTDIRDELIM(backuphome);
    if(!cptr){
      errmsg(T_("Strange error: cannot separate binary directory"));
      do_exit(5);
    }
    *cptr = '\0';
    cptr = FN_LASTDIRDELIM(backuphome);
    if(!cptr){
      errmsg(T_("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);
  confdir = strdup(libdir);

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

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

#else	/* defined(ORIG_DEFAULTS) */

  if(!backuphome){
     /* construct file- and dirnames */
     bindir = DEFBINDIR;
     vardir = DEFVARDIR;
     libdir = DEFLIBDIR;
     logdir = DEFLOGDIR;
     confdir = DEFCONFDIR;
  }
  else{
     backuphome = strdup(backuphome);
     /* construct file- and dirnames */
     /* first bin or sbin ? */
     bindir = strapp(backuphome, FN_DIRSEPSTR "bin");
     cptr = strapp(bindir, FN_DIRSEPSTR "afbackup");
     if(access(cptr, X_OK)){
	free(cptr);
	cptr = strapp(backuphome, FN_DIRSEPSTR "sbin" FN_DIRSEPSTR "afbackup");
	if(!access(cptr, X_OK)){
	  free(bindir);
	  bindir = strapp(backuphome, FN_DIRSEPSTR "sbin");
	}
     }
     ZFREE(cptr);
     vardir = strapp(backuphome, FN_DIRSEPSTR "var");
     libdir = strapp(backuphome, FN_DIRSEPSTR "lib");
     logdir = strapp(backuphome, FN_DIRSEPSTR "var");
     confdir = strapp(backuphome, FN_DIRSEPSTR "lib");
  }

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

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

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

  if((mode == MODE_UPDATE_INDEXES || mode == MODE_RESTORE) && curuid){
    if(setuid(0)){
	errmsg(T_("Error: Cannot setuid root, sorry"));
	do_exit(17);
    }
  }

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

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

  if(statb.st_uid && curuid){
    errmsg(T_("Error: Configuration file must be owned by root when running as normal user"));
    do_exit(7);
  }

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

  /* override by commandline-arguments */
  get_args(argc, argv);

#ifndef	USE_ZLIB

  if(builtin_compr_level){
    fprintf(stderr, T_("Warning: Built-in compression requested, but not compiled in. Ignoring request for built-in compression.\n"));
    builtin_compr_level = 0;
  }

#endif

  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);
  piperprogram = strapp(bindir, FN_DIRSEPSTR "__piper");
  zprogram = strapp(bindir, FN_DIRSEPSTR "__z");
  server_ids_file = strapp(vardir, FN_DIRSEPSTR "server_ids");
  index_ages_file = strapp(vardir, FN_DIRSEPSTR "index_ages");
  if(!paramfile || !partfile || !oldmarkfile || !orgoldmarkfile
		|| !clientprogram || !newmarkfile || !piperprogram
		|| ! zprogram || num_dont_compress < 0
		|| !index_ages_file || !server_ids_file)
    nomemerrexit();

  if(!cryptfile){
    cryptfile = strapp(confdir, FN_DIRSEPSTR "cryptkey");
    if(access(cryptfile, R_OK))
	ZFREE(cryptfile);
  }
  if(cryptfile)
    massage_string(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 = ' ';
  if(backupportlist && empty_string(backupportlist))
    ZFREE(backupportlist);
  if(!backupportlist)			/* set default server name */
    backupportlist = strdup(DEFAULT_SERVICE);
  while( (cptr = strchr(backupportlist, ',')) )	/* [,;] -> SPC */
    *cptr = ' ';
  while( (cptr = strchr(backupportlist, ';')) )
    *cptr = ' ';
  num_backuphosts = str2words(&backuphosts, backuphostlist);
  num_backupports = str2words(&backupportstrs, backupportlist);
  if(num_backuphosts < 0 || num_backupports < 0)
    nomemerrexit();
  backupports = ZRENEWP(backupports, Int32, num_backuphosts * sizeof(Int32));
  if(num_backupports > num_backuphosts)	/* ports: no more than hosts */
    num_backupports = num_backuphosts;
  for(i = 0; i < num_backupports; i++){
    p = get_tcp_portnum(backupportstrs[i]);
    if(p < 0){
	errmsg(T_("Error: Cannot resolve TCP service name `%s'.\n"),
				backupportstrs[i]);
	exit(1);
    }

    backupports[i] = p;
  }
  while(num_backupports < num_backuphosts)	/* set default values */
    backupports[num_backupports++] = -1;

  one_server = (num_backuphosts == 1 ? YES : NO);

  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 && daystostoreindexes >= 0.0)
    errmsg(T_("Warning: Both DaysToStoreIndexes and NumIndexesToStore configured. Ignoring the shorter duration"));
  if(numindexestostore < 0)
    numindexestostore = 0;
  if(!compresscmd || !uncompresscmd)
    uncompresscmd = compresscmd = "";
  if(!compresscmd[0] || !uncompresscmd[0])
    uncompresscmd = compresscmd = "";
  EEM__(repl_dirs(&compresscmd) || repl_dirs(&uncompresscmd)
	|| repl_dirs(&idxcompresscmd) || repl_dirs(&idxuncompresscmd));
  /* check, if compress/uncompress commands are executable */
  cptr = check_commands_executable(YES, compresscmd, uncompresscmd,
		idxcompresscmd, idxuncompresscmd, NULL);
  if(cptr){
    errmsg(T_("Error: Cannot execute `%s'"), cptr);
    exit(43);
  }

  if(!builtin_compr_level){
    if(!idxcompresscmd)
	idxcompresscmd = compresscmd;
    if(!idxuncompresscmd)
	idxuncompresscmd = uncompresscmd;
  }
  else{
    /* Instead of hacking all the zlib stuff in here i construct
     * pipes through the __z program, that does the same as the
     * built-in zlib compression. This is much easier to implement,
     * much less error prone and affects performance only little,
     * because the subprocesses are started only few times */

    sprintf(tmpbuf, "%d", (int) builtin_compr_level);
    if(compresscmd[0]){
	if(!idxcompresscmd)
	  idxcompresscmd = strchain(piperprogram, " ", compresscmd, " | ",
				zprogram, " -", tmpbuf, NULL);
	compresscmd = strchain(". ", tmpbuf, " ", compresscmd, NULL);
    }
    else{
	if(!idxcompresscmd)
	  idxcompresscmd = strchain(zprogram, " -", tmpbuf, NULL);
	compresscmd = strapp(". ", tmpbuf);
    }

    if(uncompresscmd[0]){
	if(!idxuncompresscmd)
	  idxuncompresscmd = strchain(piperprogram, " ", zprogram, " -d | ",
				uncompresscmd, NULL);
    }
    else{
	if(!idxuncompresscmd)
	  idxuncompresscmd = strapp(zprogram, " -d");
	uncompresscmd = ".";				/* a dummy */
    }
  }
  if(!compresscmd[0] || !uncompresscmd[0]){
    compressbu = NO;
  }
  if(!idxcompresscmd || !idxuncompresscmd)
    idxuncompresscmd = idxcompresscmd = "";
  if(!idxcompresscmd[0] || !idxuncompresscmd[0])
    idxuncompresscmd = idxcompresscmd = "";
  if(!idxcompresscmd[0] || !idxuncompresscmd[0]){
    compresslogfiles = NO;
  }
  if(!logfile)
    logfile = "";
  massage_string(logfile);
  if(!logfile[0])
    EM__(logfile = strapp(logdir, FN_DIRSEPSTR "backup.log"));
  if(startinfoprog){
    if(empty_string(startinfoprog)){
	ZFREE(startinfoprog);
    }
  }
  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);
    }
  }
  if(exclude_filename){
    if(empty_string(exclude_filename)){
	ZFREE(exclude_filename);
    }
    else
	massage_string(exclude_filename);
  }
  massage_string(rootdir);
  massage_string(indexfilepart);
  if(identity){
    if(empty_string(identity)){
	ZFREE(identity);
    }
    else
	massage_string(identity);
  }
  if(!identity)
    identity = systemname;

  EEM__(repl_dirs(&piperprogram) || repl_dirs(&logfile)
	|| repl_dirs(&cryptfile) || repl_dirs(&indexfilepart)
	|| repl_dirs(&initprog) || repl_dirs(&exitprog)
	|| repl_dirs(&startinfoprog) || repl_dirs(&lockfile));

  EM__(reportfile = tmp_name(FN_TMPDIR FN_DIRSEPSTR "afbrep"));

  /* check encryption keyfile */
  if(cryptfile && (i = check_cryptfile(cryptfile)) ){
    if(i > 0)
	logmsg(T_("Warning concerning encryption key file \"%s\": %s"),
			cryptfile, check_cryptfile_msg(i));
    else
	errmsg(T_("Error concerning encryption key file \"%s\": %s"),
			cryptfile, check_cryptfile_msg(i));

    if(i < 0)
	exit(11);

    logmsg(T_("Warning: Ignoring file \"%s\", using compiled-in key"),
			cryptfile);
    ZFREE(cryptfile);
  }

  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);

  if(mode == MODE_COPY_TAPE)
    copy_tape(argc, argv);	/* will exec */

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

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

	fprintf(stderr, T_("Please check if this process is an obstacle to continue.\n"));
	fprintf(stderr, T_("Do you want to continue anyway ? (y/N) "));
	if(!isatty(0)){
	  errmsg(T_("\nCannot read confirmation from TTY. Exiting"));
	  exit(39);
	}
	if(wait_for_input(0, 120) < 1){
	  fprintf(stderr, T_("\nTimeout on input, exiting.\n"));
	  exit(40);
	}

	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(T_("Warning: Cannot remove lockfile \"%s\""), lockfile);
    }
    lck = set_lock();
  }

  if(lck != BU_GOT_LOCK){
	fprintf(stderr, T_("Warning: Cannot set lock. Continue anyway ? (y/N) "));
	if(!isatty(0)){
	  fprintf(stderr, T_("\nCannot read confirmation from TTY. Exiting.\n"));
	  exit(38);
	}
	if(wait_for_input(0, 120) < 1){
	  fprintf(stderr, T_("Timeout on input, exiting.\n"));
	  exit(41);
	}

	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_RESTORE || mode == MODE_VERIFYBU
		|| mode == MODE_UPDATE_INDEXES){
    if( (i = read_pint_file(numfile, &num)) )
	cptr = numfile;

    last_idx_num = num;

    if(mode == MODE_RESTORE || mode == MODE_VERIFYBU
				|| mode == MODE_UPDATE_INDEXES){
      if(! restore_em && ! restore_emlist && ! restore_EM){
	if(i){
	  errmsg(T_("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(T_("Warning: Cannot read file \"%s\", switching to full backup"),
				cptr);
	mode = MODE_FULL_BACKUP;
      }
    }
  }

  part = 1;
  if(numparts > 1){
    ParamFileEntry	*dirsparams;
    UChar		**partdirstobackupraw = NULL, *cptr;

    read_pint_file(partfile, &part);

    if(mode == MODE_FULL_BACKUP){
      part++;
      if(part > numparts)
	part = 1;
    }
  }

  /* read the number of the backup, if file present */
  if(mode == MODE_FULL_BACKUP){
    if(read_pint_file(numfile, &num))	/* set num to 0 if file absent */
      part = 1;

    last_idx_num = num;

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

  if(daystostoreindexes >= 0.0){	/* set numindexestostore from it */
    time_t	read_time, cmp_time;

    cmp_time = time(NULL) - (time_t)(daystostoreindexes * 86400.0);

    for(i = num - 1; ; i--){
      read_time = (time_t) 0;

      sprintf(tmpbuf, "%d:", (int) i);
      cptr = kfile_get(index_ages_file, tmpbuf, KFILE_LOCKED);

      if(cptr){
	read_time = strint2time(cptr);
	free(cptr);
      }
      else{	/* timestamp not in index ages file -> read index to find the */
	pid = -1;		/* latest timestamp in there, save and use it */
	fd = open_idxfile_read(i, &pid, NULL, YES);
	if(fd >= 0){
	  cptr = NULL;
	  n = 0;
	  fp = fdopen(fd, "r");
	  while(!feof(fp)){
	    if(fget_realloc_str(fp, &cptr, &n))
		continue;

	    if(INDEX_COMMENT(
		eval_index_line(cptr, NULL, NULL, NULL, NULL, NULL, NULL)) &&
				!strncmp(first_nospace(cptr), "##", 2))
	      read_header_line(cptr, NULL, &read_time, NULL, NULL, NULL);
	  }

	  ZFREE(cptr);
	  fclose(fp);
	  if(pid >= 0)
	    waitpid(pid, &pst, 0);

	  if(read_time != (time_t) 0){
	    sprintf(tmpbuf, "%d:", (int) i);
	    time_t_to_intstr(read_time, tmpbuf + 20);

	    kfile_insert(index_ages_file, tmpbuf, tmpbuf + 20,
						KFILE_SORTN | KFILE_LOCKED);
	  }
	}
      }

      if(read_time < cmp_time){
	i = num - 1 - i;

	if(i > numindexestostore)
	  numindexestostore = i;

	break;
      }
    }
  }

  if(mode == MODE_RESTORE)
    restore(argc, argv);		/* will exit */

  save_entry_file = strapp(vardir, FN_DIRSEPSTR SAVE_ENTRIES_FILE);
  level_times_file = strapp(vardir, FN_DIRSEPSTR LEVEL_TIMESTAMPS_FILE);
  needed_tapes_file = strapp(vardir, FN_DIRSEPSTR NEEDED_TAPES_FILE);
  start_pos_file = strapp(vardir, FN_DIRSEPSTR START_POSITIONS_FILE);
  tmp_rm_on_bu_file = strapp(vardir, FN_DIRSEPSTR TMP_RM_ON_BACKUP_FILE);
  if(!save_entry_file || !level_times_file || !needed_tapes_file
			|| !start_pos_file|| !tmp_rm_on_bu_file)
    nomemerrexit();

  if(mode == MODE_UPDATE_INDEXES)
    goto poll_servers;		/* skip a lot of stuff */

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

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

  if(mode == MODE_INCR_BACKUP && backup_level == MAXINT)
    backup_level = 0;

  if(numparts > 1){
    ParamFileEntry	*dirsparams;
    UChar		**partdirstobackupraw = NULL, *cptr;

    /* collect all stuff, that must be backuped */
    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;

	partdirstobackupraw[i] = strdup("");
    }

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

    ZFREE(alldirstobackupraw);
    alldirstobackupraw = strdup("");

    for(i = 0; i < numparts; i++){
	replace_if_pipe(&(partdirstobackupraw[i]), mode, backup_level, part);

	cptr = strchain(alldirstobackupraw, " ",
					partdirstobackupraw[i], NULL);
	free(alldirstobackupraw);
	alldirstobackupraw = cptr;
	free(partdirstobackupraw[i]);
	free(dirsparams[i].pattern);
    }
    free(partdirstobackupraw);
    free(dirsparams);
  }
  else{
    replace_if_pipe(&dirstobackupraw, mode, backup_level, 0);

    EM__(alldirstobackupraw = strdup(dirstobackupraw));
  }

  ZFREE(dirstobackupraw);
  EM__(dirstobackupraw = strdup(alldirstobackupraw));

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

    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(T_("Error: Cannot read parameter file"));
	do_exit(6);
    }
    free(dirsparam.pattern);

    if(partdirstobackupraw){
	ZFREE(dirstobackupraw);

	dirstobackupraw = partdirstobackupraw;

	replace_if_pipe(&dirstobackupraw, mode, backup_level, part);
    }
  }

  if(!access(save_entry_file, R_OK)){
    EM__(save_entries = read_asc_file(save_entry_file, NULL));
  }

  if(!access(level_times_file, R_OK)){
    EM__(level_timespecs = kfile_getall(level_times_file,
				 &num_level_timespecs, KFILE_LOCKED));
  }


  /* 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
	|| str2wordsq(&alldirstobackup, alldirstobackupraw ? alldirstobackupraw : ES) < 0)
    nomemerrexit();

  dirlists[0] = dirstobackup;
  dirlists[1] = alldirstobackup;

  for(cpptr = dirstoskip; *cpptr; cpptr++)
    cleanpath(*cpptr);
  for(cpptr = filestoskip; *cpptr; cpptr++)
    cleanpath(*cpptr);

  for(cppptr = dirlists; *cppptr; cppptr++){
    for(cpptr = *cppptr; *cpptr; cpptr++){
      if(!strncmp(*cpptr, FILECONTPREFIX, i = strlen(FILECONTPREFIX))){
	cleanpath(*cpptr + i);
      }
      else if(!strncmp(*cpptr, FILECONTZPREFIX, i = strlen(FILECONTZPREFIX))){
	cleanpath(*cpptr + i);
      }
      else if(!strncmp(*cpptr, LOCALDEVPREFIX, i = strlen(LOCALDEVPREFIX))){
	cleanpath(*cpptr + i);
      }
      else{
	cleanpath(*cpptr);
      }
    }

#if 0	/* don't do this: it's dangerous, when paths are symlinked or cross-filesystem */
/* FIXME: work this through for dirstobackup -> *cppptr */
  /* 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 = *cppptr; *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 = *cppptr, n = 0; *cpptr; cpptr++, n++);

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

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

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

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

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

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

     default:
      break;
    }
  }

  /* the timestamp is kept, if not the standard files and directories
   * from the comfiguration file are saved, but something else expli-
   * citely given at the command line.  */
  if(no_default_backup || (mode == MODE_INCR_BACKUP && keep_counter))
    keep_timestamp = YES;

  /* Furthermore, if not set due to the above conditions and if full
   * backup is split in pieces, when saving one of these pieces the
   * timestamp is also kept, if an old timestamp already exists.
   * Reason: If a file is modified/created between an incremental
   * and a following part of the full backup and the timestamp
   * changes then, the file will not be saved before the full save
   * of the piece containing that file. This might be quite a long
   * time later, too long to keep the file unsaved. Thus keeping
   * the timestamp here makes files probably saved several times
   * unnecessarily and wastes space, but this behaviour is safer.
   * If no old timestamp exists, one is created nonetheless. */
  if(mode == MODE_FULL_BACKUP && numparts > 1 && !keep_timestamp){
    keep_timestamp = YES;

    if(access(oldmarkfile, F_OK))	/* no old timestamp there */
	keep_timestamp = NO;		/* force creation of a new one */
  }

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

  start_time = time(NULL);

  /* 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(T_("Error: Cannot rename file \"%s\""), oldmarkfile);
	failed_exit(9);
      }
    }

    fp = fopen(oldmarkfile, "w");
    if(!fp){
      errmsg(T_("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(T_("Error: Cannot create file \"%s\""), newmarkfile);
      failed_exit(10);
    }
    fclose(fp);
  }

  if(interrupted)
    intrpt_exit();

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

  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);
    }
  }

  unzipfile = hidden_filename(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;
  unzipfilecmd = NULL;
  inunzip = outzip = 0;
  ifd = lfd = ipid = opid = -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;
    }
		/* check for hidden file containing the unprocesscmd */
    if(!access(unzipfile, R_OK)){
	unzipfilecmd = read_asc_file(unzipfile, 0);
    }
  }

  outzip = (compresslogfiles && idxcompresscmd[0] ? 1 : 0);
  outfile = (outzip ? (inunzip ? tmpidxf : zippedidxf) : indexfile);
  filelist = outfile;

  uncompcmd = (idxuncompresscmd[0] ? idxuncompresscmd : (UChar *) "gunzip");


 poll_servers:
  server_id_tab = NEWP(ServerIdTab, num_backuphosts);
  if(!server_id_tab)
    nomemerrexit();
  memset(server_id_tab, 0, sizeof(ServerIdTab) * num_backuphosts);

  stst = NEWP(StreamerStat, num_backuphosts);
  memset(stst, 0, num_backuphosts * sizeof(StreamerStat));

  num_server_ids = num_servers = 0;

  /* now we poll the servers, if there are several */
    /* Cause below we are asking the determined server some more things now, */
    /* (get_tapepos) and the 5 seconds interval is then kept, the following */
    /* arguments are no longer valid. So we can open the old and new indexes */
    /* after polling the server. This was done before in earlier versions, */
    /* cause it could have taken notable time, especially > 5 seconds. */
    /* *** start of obsolete argumentation *** */
    /* 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) */
  forever{
    for(serveridx = 0; serveridx < num_backuphosts; serveridx++){
	if(interrupted)
	  break;

	n = get_streamerstate(stst + serveridx, NULL, 0);
	if(n < 0){		/* fatal error occurred, not retryable */
	  memmove(backuphosts + serveridx, backuphosts + serveridx + 1,
			(num_backuphosts - serveridx - 1) * sizeof(*backuphosts));
	  memmove(backupports + serveridx, backupports + serveridx + 1,
			(num_backupports - serveridx - 1) * sizeof(*backupports));
	  memmove(cartsets + serveridx, cartsets + serveridx + 1,
			(num_cartsets - serveridx - 1) * sizeof(*cartsets));
	  serveridx--;
	  num_backuphosts--;
	  num_servers--;
	  if(num_backuphosts <= 0){
	    errmsg(T_("Error: No server can be contacted, fatal: exiting"));
	    failed_exit(36);
	  }
	}
	if(n)
	  continue;

	num_servers = MAX(num_servers, serveridx + 1);

	if(num_server_ids < num_backuphosts){
	  server_id_tab[num_server_ids].servername = backuphosts[serveridx];
	  server_id_tab[num_server_ids].serverport = backupports[serveridx];
	  server_id_tab[num_server_ids].serverid = stst[serveridx].serverid;
	  num_server_ids++;
	}

	if(one_server)
	  break;
	
	i = stst[serveridx].devstatus & STREAMER_STATE_MASK;

	if(i == STREAMER_READY)
	  break;

	if(i == STREAMER_UNLOADED
		&& (stst[serveridx].devstatus & STREAMER_CHANGEABLE)){
	  n = get_tapepos(&i, NULL, NULL,
		newcart ? INSERT_POS_NEWCART : INSERT_POSITION, NULL, 0, NULL);
	  if(n)
	    continue;

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

    if(serveridx < num_backuphosts || interrupted || one_server)
	break;

    /* none found up to here, so we take the one with the lowest load */
    i = -1;
    for(serveridx = 0; serveridx < num_backuphosts; serveridx++){
	if(stst[serveridx].serverstatus == OPENED_FOR_WRITE){
	  n = stst[serveridx].numactive + stst[serveridx].numpending;
	  if(i < 0){
	    i = n;
	    j = serveridx;
	  }
	  else{
	    if(n < i){
		i = n;
		j = serveridx;
	    }
	  }
	}
    }

    if(i >= 0){
	serveridx = j;
	break;
    }

    ms_sleep(1000 * 30);
  }

		/* update the file containing the found assignments */
		/* of hostname/port-pairs to server-IDs */
  add_server_id_entries(server_ids_file, server_id_tab, num_server_ids);

  for(i = 0; i < num_servers - 1; i++){		/* pack duplicate entries */
    for(j = i + 1; j < num_servers; j++){
	if(!strcmp(stst[i].serverid, stst[j].serverid)){
	  merge_Uns32Ranges(&(stst[i].needed_tapes), stst[j].needed_tapes);
	  free(stst[j].needed_tapes);
	  num_servers--;
	  memmove(stst + j, stst + j + 1, sizeof(stst[0]) * (num_servers - j));
	  j--;
	}
    }
  }
		/* find out, whether tapes have been erased on the server */
  for(i = 0; i < num_servers; i++){
    Uns32Range	*needed_tapes;

    if(!stst[i].needed_tapes)
	continue;

    needed_tapes = get_needed_tapes(needed_tapes_file, stst[i].serverid);
    if(!needed_tapes)
	continue;
					/* determine the difference set */
    needed_tapes = del_range_from_Uns32Ranges(needed_tapes, stst[i].needed_tapes);
    if(len_Uns32Ranges(needed_tapes) > 0){
			/* remove the erased tapes from the needed-tapes-file */
	if(remove_needed_tapes(needed_tapes_file, stst[i].serverid, needed_tapes)){
	  errmsg(T_("Error: Could not remove deleted cartridges from file %s"),
			needed_tapes_file);
	}

	cptr = str_Uns32Ranges(needed_tapes, 0);	/* print message */
	logmsg(T_("Info: The following tapes have been erased on the server with ID %s: %s. The indexes will be updated"),
			stst[i].serverid, cptr);
	free(cptr);
    }
    else
	ZFREE(needed_tapes);

    stst[i].deleted_tapes = needed_tapes;
  }
	/* and remove the deleted tapes from the indexes */
  i = remove_tapes_from_idxes(stst, num_servers, server_ids_file,
				last_idx_num);
  if(mode == MODE_UPDATE_INDEXES)
    exit(ABS(i));

  ZFREE(server_id_tab);

  /* now open index files, copy old on incr/level-X backup */
  if(infile){
    cptr = inunzip ? uncompcmd : NULL;

    if(unzipfilecmd)
	if(unzipfilecmd[0])
	  cptr = unzipfilecmd[0];

    ifd = open_from_pipe_sigblk(cptr, infile, 1, &ipid, &idxproc_blk_sigs);
    if(ifd < 0){
	errmsg(T_("Warning: Cannot read filename logfile \"%s\""), infile);
    }
  }
  lfd = open_to_pipe_sigblk(outzip ? idxcompresscmd : NULL, outfile, 1 + 2,
			&opid, 0600, &idxproc_blk_sigs);
  if(lfd < 0){
    errmsg(T_("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_forced(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(T_("Warning: Errors occurred reading \"%s\""), infile);
      }
    }
  }

  if(interrupted)
    intrpt_exit();

  used_port = backupports[serveridx] > 0 ? backupports[serveridx] : DEFAULT_PORT;

  SETZERO(scandir_args);

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

	close(ppo[0]);
	close(ppe[0]);
	close(ppi[1]);
	dup2(ppi[0], 0);
	dup2(ppo[1], 1);
	dup2(ppe[1], 2);

	sigemptyset(&blocked_signals);	/* client should terminated cleanly */
	sigaddset(&blocked_signals, SIGINT);
	sigaddset(&blocked_signals, SIGTERM);
	sigprocmask(SIG_SETMASK, &blocked_signals, NULL);

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

	cpptr = (UChar **) clargv;
	*(cpptr++) = FN_BASENAME(clientprogram);
	*(cpptr++) = "-QwjcvnlOUE";
	if(newcart){
	  *(cpptr++) = "-G";
	}
	*(cpptr++) = "-h";
	*(cpptr++) = backuphosts[serveridx];
	*(cpptr++) = "-H-";
	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(identity){
	  *(cpptr++) = "-W";
	  *(cpptr++) = identity;
	}
	if(reportfile){
	  if(reportfile[0]){
	    *(cpptr++) = "-V";
	    *(cpptr++) = reportfile;
	  }
	}
	if(chksum){
	  *(cpptr++) = "-Z";
	}

	*cpptr = NULL;
	execv(clientprogram, clargv);

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

  close(ppi[0]);
  close(ppo[1]);
  close(ppe[1]);

  errflp = NO;

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

#if	! defined(O_NONBLOCK) && ! defined(O_NDELAY)
#error Nor O_NONBLOCK neither O_NDELAY is defined. Need to set filedescriptor to non blocking mode.
#endif

  ofdflags = fdflags = fcntl(ppo[0], F_GETFL);
  eofdflags = efdflags = fcntl(ppo[0], F_GETFL);
#ifdef	O_NONBLOCK
  fdflags |= O_NONBLOCK;
  efdflags |= O_NONBLOCK;
#endif
#ifdef	O_NDELAY
  fdflags |= O_NDELAY;
  efdflags |= O_NDELAY;
#endif
  fcntl(ppo[0], F_SETFL, fdflags);
  fcntl(ppe[0], F_SETFL, efdflags);

  scandir_args.fd = lfd;
  scandir_args.ctofd = ppi[1];
  scandir_args.cfromfd = ppo[0];
  scandir_args.cerrfd = ppe[0];
  EM__(scandir_args.read_data = NEWP(UChar, 8192));
  scandir_args.read_data_nall = 8192;
  EM__(scandir_args.read_data = NEWP(UChar, 1024));
  scandir_args.read_data_nall = 1024;

  strcpy(server_id, "");
  n = 0;
  forever{
    i = read(ppo[0], scandir_args.read_data + n, 8191 - n);
    if(i > 0)
	n += i;
    else{
	wait_for_input(ppo[0], 1);

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

    scandir_args.read_data[n] = '\0';

    i = j = 0;		/* parse output of the client to get data for the */
    cptr = scandir_args.read_data;		/* minimum restore info */
    forever{
	cptr2 = strchr(cptr, '\n');
	if(cptr2)
	  *cptr2 = '\0';

	if(re_match(&cartridge_re, cptr, strlen(cptr), 0, NULL) >= 0)
	  i = trailingint(cptr);
	if(re_match(&file_re, cptr, strlen(cptr), 0, NULL) >= 0)
	  j = trailingint(cptr);
	if(!strncmp(cptr, "Server-ID:", 10))
	  strcpy(server_id, first_nospace(cptr + 10));
	if(!strcmp(cptr, "--") && i && j)
	  n = -1;		/* endmarker */

	if(cptr2)
	  *cptr2 = '\n';

	if(n < 0 || !cptr2)
	  break;

	cptr = cptr2 + 1;
    }

    if(i && j && strcmp(server_id, "")){
	startcart = i;
	startfile = j;
	break;
    }
  }
			/* name for the temporary copy of start_positions */
  EM__(tmpstartposfile = tmp_name(FN_TMPDIR FN_DIRSEPSTR "afbsp_"));

  lines = NULL;	/* now save the tape write start position information */
  n = 0;
  if(!access(start_pos_file, R_OK)){	/* read startpos file if existing */
    EM__(lines = read_asc_file(start_pos_file, &n));

    for(i = 0; i < n; i++){
	j = sscanf(lines[i], "%d", &i1);	/* throw out dated lines */

	if(j > 0 && i1 <= (int)(num - (numindexestostore + 1))){
	  free(lines[i]);
	  memmove(lines + i, lines + i + 1, sizeof(UChar *) * (n - i));
	  i--;
	  n--;
	}
    }
  }

  EM__(lines = ZRENEWP(lines, UChar *, n + 2));	/* add a new line */

  sprintf(tmpbuf, "%d.%d/%d: %s %d %d %d",
			(int) num, (int) part, (int) numparts,
			backuphosts[serveridx], (int) used_port,
			(int) startcart, (int) startfile);
  EM__(lines[n] = strdup(tmpbuf));
  lines[n + 1] = NULL;

  if(write_asc_file_safe(start_pos_file, lines, 0)){	/* save the file */
    errmsg(T_("Error: Cannot write file `%s' to save the backup start positions"),
				start_pos_file);
  }
  free_asc_file(lines, 0);
				/* create a copy with unique name */
  if(copy_file(start_pos_file, tmpstartposfile)){
    errmsg(T_("Warning: Cannot make copy of file `%s', saving original.\n"),
			start_pos_file);
    free(tmpstartposfile);

    tmpstartposfile = start_pos_file;
  }
				/* construct the minimum restore info */
  sprintwordq(tmpbuf, identity);
  i = strlen(EMREC_PREFIX) + strlen(tmpbuf) + strlen(backuphosts[serveridx])
					+ 50 + strlen(tmpstartposfile);
  EM__(minrestoreinfo = NEWP(UChar, i));
  sprintf(minrestoreinfo, "%s %s %s %d %d %d %s", EMREC_PREFIX,
			tmpbuf, backuphosts[serveridx], (int) used_port,
			(int) startcart, (int) startfile, tmpstartposfile);

		/* construct the header information, first for the index */
  cptr = tmpbuf;
  if(mode == MODE_INCR_BACKUP || (mode == MODE_FULL_BACKUP && part > 1))
    *(cptr++) = '\n';

  sprintf(cptr, "%s%s%d%s%d.%d:\n",
		backuphosts[serveridx], PORTSEP, (int) used_port, LOCSEP,
		(int) startcart, (int) startfile);
  cptr += strlen(cptr);
  sprintf(cptr, "## %s backup at %s, current number: %d, hostname: %s client id: ",
		mode == MODE_INCR_BACKUP ? "Incremental" : "Full",
		(char *) actimestr(), (int) num, (char *) systemname);
  sprintwordq(cptr + strlen(cptr), identity);
  strcat(cptr, "\n");

  write_forced(lfd, tmpbuf, strlen(tmpbuf));	/* write it to the index */

		/* construct header data including minimum restore info */
  EM__(cptr2 = mk_esc_seq(cptr - 1, ESCAPE_CHARACTER, NULL));
				/* -1 cause a leading newline is required */
  cptr = actimestr();
  EM__(header = NEWP(UChar, 3 * (strlen(EMREC_TAPE_PREFIX) + 50 + strlen(cptr)
		+ strlen(minrestoreinfo) + strlen(EMREC_POSTFIX))
		* (1 + strlen("\\b")) + strlen(cptr2)));
  strcpy(header, "");

  sprintf(tmpbuf, "%s %s %s", cptr, minrestoreinfo, EMREC_POSTFIX);
  for(i = 0; i < 3; i++){
    sprintf(header + strlen(header), "%s %d %d %s\\n",
			EMREC_TAPE_PREFIX, (int) strlen(tmpbuf),
			(int) parity_byte(tmpbuf, strlen(tmpbuf)), tmpbuf);
  }

  EM__(cptr = strdup(header));
  repl_esc_seq(cptr, ESCAPE_CHARACTER);
  n = strlen(cptr);
  free(cptr);

  for(i = 0; i < n; i++)
    strcat(header, "\\b");
  strcat(header, cptr2);
  free(cptr2);

  write_forced(ppi[1], header, strlen(header));	/* write the header to the */
  write_forced(ppi[1], "\n", 1);	/* client program, then write the */
				/* filename to the client program to save it */
  success = (success &&
	(! write_filename(tmpstartposfile, (void *) (& scandir_args), NULL)));

  cmp_time = (time_t) 0;

  if(mode == MODE_INCR_BACKUP){
    stat(oldmarkfile, &statb);
    cmp_time = statb.st_mtime;
  }

  if(backup_level > 0){		/* backup level is given (or full_backup) */
    if(level_timespecs){	/* if we have read a file with timestamps */
	long int	l;
	time_t		rtime, latest_time = 0;

	for(i = 0; i < num_level_timespecs; i++){	/* search the entry */
	  if(sscanf(level_timespecs[i].key, "%ld", &l) < 1)
	    continue;	/* with level >= given and the latest timestamp */

	  if(l >= backup_level){
	    rtime = time_from_datestr(level_timespecs[i].value);
	    if(rtime == (time_t) -1){
		errmsg(T_("Warning: Format error in level timestamps file. Ignoring Level %ld.\n"), l);
		continue;
	    }

	    if(rtime > latest_time)
		latest_time = rtime;
	  }
	}

	if(latest_time > 0)
	  cmp_time = latest_time;

	kfile_freeall(level_timespecs, num_level_timespecs);
    }

    if(backup_level == MAXINT)
	cmp_time = 0;
  }

  SETZERO(findparams);
  findparams.excl_dirs = dirstoskip;
  findparams.excl_names = filestoskip;
  findparams.errfunc = err_write_filename;
  findparams.errfunc_param = (void *) (& scandir_args);
  findparams.excl_filename = exclude_filename;
  findparams.newer_than = cmp_time;
  findparams.options = FIND_DEPTH;
  findparams.interrupted = &interrupted;

  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;

      if(save_entries && mode == MODE_INCR_BACKUP){
	i = 0;
	for(cpptr2 = save_entries; *cpptr2; cpptr2++)
	  if(!strcmp(*cpptr, *cpptr2))
	    i = 1;

	if(!i){
	  findparams.newer_than = (time_t) 0;
	  logmsg(T_("Info: New entry, making full backup of \"%s\""), *cpptr);
	}
      }

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

      cpptr3 = fnglob(dirsarr[0]);
      if(cpptr3){
	if(! *cpptr3)
	  ZFREE(cpptr3);
      }
      if(!cpptr3)
	logmsg(T_("Warning: Entry `%s' did not resolve any filename"),
			dirsarr[0]);
      else
	free_array(cpptr3, 0);

      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 *) (& scandir_args), NULL);

      if(interrupted)
	break;
    }

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

    break;
  }

  close(ppi[1]);

  fcntl(ppo[0], F_SETFL, ofdflags);
  fcntl(ppe[0], F_SETFL, eofdflags);

  success = (success && (!write_filename(NULL, (void *) (& scandir_args), NULL)));

  waitpid_forced(pid, &pst, 0);
  errflp |= WEXITSTATUS(pst);
  close(ppo[0]);
  close(ppe[0]);

  close(lfd);
  if(opid >= 0){
    waitpid_forced(opid, &pst, 0);
    errflp |= WEXITSTATUS(pst);
  }

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

  if(must_read_fnamlog){
    num_outlines = 0;
    if(inunzip || outzip){
      ipid = -1;			/* count written lines */
      cptr = outzip ? (idxuncompresscmd[0] ? idxuncompresscmd : (UChar *) "gunzip") : NULL;
      filename = (inunzip && outzip) ? tmpidxf : outfile;
      i = stat(filename, &statb);
      if(i){
	errmsg(T_("Warning: Cannot read filename logfile \"%s\""), filename);
      }
      if(!i && statb.st_size > 0){
	ifd = open_from_pipe_sigblk(cptr, filename, 1,
					&ipid, &idxproc_blk_sigs);
	if(ifd < 0){
	  errmsg(T_("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, 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(T_("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 = NO;			/* 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(outzip){
	free_asc_file(unzipfilecmd, 0);
	unzipfilecmd = NEWP(UChar *, 2);
	unzipfilecmd[0] = uncompcmd;
	unzipfilecmd[1] = NULL;

	write_asc_file_safe(unzipfile, unzipfilecmd, 1);
    }
  }

  if(interrupted)
    intrpt_exit();

  if(! success){
    errmsg(T_("Error: An error occurred during full backup"));
    errmsg(T_("The logfile `%s' should tell more details"), logfile);
    failed_exit(14);
  }

  if(!success)
    failed_exit(15);

				/* now we know, that everything worked fine */
  lines = read_asc_file(tmp_rm_on_bu_file, NULL);
  if(lines){					/* cleanup temp files from */
    for(cpptr = lines; *cpptr; cpptr++)		/* earlier activities */
	unlink(*cpptr);
    free_asc_file(lines, 0);

    unlink(tmp_rm_on_bu_file);
  }

  if(strcmp(start_pos_file, tmpstartposfile)){
    UChar	*llines[2];

    llines[0] = tmpstartposfile;	/* save filename to be removed later */
    llines[1] = NULL;
    write_asc_file_safe(tmp_rm_on_bu_file, llines, 0);

    free(tmpstartposfile);
  }

  if(startinfoprog){
    fp = popen(startinfoprog, "w");
    fprintf(fp, "%s\n", minrestoreinfo);
    pclose(fp);
  }

  sprintf(tmpbuf, "%d:", (int) num);
  time_t_to_intstr(time(NULL), tmpbuf + 20);
  if(kfile_insert(index_ages_file, tmpbuf, tmpbuf + 20,
				KFILE_SORTN | KFILE_LOCKED)){
    errmsg(T_("Error: Could not update index ages file `%s'"),
			index_ages_file);
  }

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

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

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

    if(!keep_timestamp)
	unlink(orgoldmarkfile);

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

      i = strlen(indexfile) + 30;
      if(i < TMPBUFSIZ)
	cptr = tmpbuf;
      else
	cptr = NEWP(UChar, i);
      j = 1;		/* flag: more lines to be removed in index ages */
      if(cptr){
	while(n > 0){
	  sprintf(cptr, "%s%d", indexfilepart, (int) n);
	  unlink(cptr);
	  strcat(cptr, COMPRESS_SUFFIX);
	  unlink(cptr);
	  filename = hidden_filename(cptr);
	  if(filename)
	    unlink(filename);
	  free(filename);

	  if(j){
	    sprintf(tmpbuf, "%d:", (int) n);
	    cptr2 = kfile_get(index_ages_file, tmpbuf, KFILE_LOCKED);
	    if(cptr2){
		kfile_delete(index_ages_file, tmpbuf, KFILE_LOCKED);
		free(cptr2);
	    }
	    else{
		j = 0;
	    }
	  }

	  n--;
	}
      }
    }
  }

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

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

  if(!keep_timestamp && backup_level > 0){
    sprintf(kbuf, "%ld:", backup_level);
    sprintf(vbuf, "%s", ctime(&start_time));
    massage_string(vbuf);

    if(kfile_insert(level_times_file, kbuf, vbuf, KFILE_SORTN | KFILE_LOCKED))
	errmsg(T_("Error: Cannot write backup level timestamp file `%s'"),
						level_times_file);
  }

  effective_backup_level = backup_level;
  remove_lower_levels = NO;
  remove_previous_equal_level = NO;

#if	0	/* never remove lower levels (would require index update) */
  if(keep_timestamp){
    remove_lower_levels = YES;
    remove_previous_equal_level = YES;
  }
#endif

  if(no_default_backup){
    effective_backup_level = MAXINT;	/* like full_backup: never remove */
    remove_lower_levels = NO;		/* entries in needed_tapes */
    remove_previous_equal_level = NO;
  }

  fp = fopen(save_entry_file, "w");
  if(fp){
    for(cpptr = alldirstobackup; *cpptr; cpptr++)
	fprintf(fp, "%s\n", *cpptr);
    fclose(fp);
  }
  else{
    errmsg(T_("Warning: Cannot save entries to file \"%s\".\n"),
			save_entry_file);
  }

  if(!access(needed_tapes_file, R_OK)){		/* save crucial tapes  */
    EM__(needed_tapes_lines = read_asc_file(needed_tapes_file,
					&num_needed_tapes_lines));
  }

  EM__(lines = read_asc_file(reportfile, NULL));
  for(cpptr = lines; *cpptr; cpptr++){
    cptr = T_("Used Cartridges:");
    if(!strncasecmp(*cpptr, cptr, strlen(cptr)))
	break;
  }

  if(*cpptr){
    cptr = strword(*cpptr, -1);
    EM__(new_tape_ranges = sscan_Uns32Ranges(cptr, 0, 0, NULL, NULL));
    free(cptr);

    if(server_id[0]){
	for(i = 0; i < num_servers; i++)
	  if(!strcmp(server_id, stst[i].serverid))
	    break;
    }

    if(needed_tapes_lines){
      for(i = 0; i < num_needed_tapes_lines; i++){
	n = sscanf(needed_tapes_lines[i], "%d", &i1);

	if(n > 0){
	  if(i1 == (int) num)
	    break;

	  if(i1 <= (int)(num - (numindexestostore + 1))){
	    free(needed_tapes_lines[i]);
	    memmove(needed_tapes_lines + i, needed_tapes_lines + i + 1,
			sizeof(UChar *) * (num_needed_tapes_lines - i));
	    num_needed_tapes_lines--;
	    i--;
	  }
	}
      }
    }

    cptr = str_Uns32Ranges(new_tape_ranges, 0);
    free(new_tape_ranges);
    sprintf(tmpbuf, " %d>%s", (int) effective_backup_level, cptr);
    free(cptr);
    if(server_id[0]){
	strcat(tmpbuf, "@");
	strcat(tmpbuf, server_id);
    }
    cptr = strdup(tmpbuf);
    sprintf(tmpbuf, "%d:", (int) num);

    if(needed_tapes_lines && i < num_needed_tapes_lines){
	n = i;
	cptr2 = strapp(needed_tapes_lines[n], cptr);
	free(needed_tapes_lines[n]);
	needed_tapes_lines[n] = cptr2;
    }
    else{
	EM__(needed_tapes_lines = ZRENEWP(needed_tapes_lines, UChar *,
					num_needed_tapes_lines + 2));
	n = num_needed_tapes_lines++;
	needed_tapes_lines[n] = strapp(tmpbuf, cptr);
	needed_tapes_lines[num_needed_tapes_lines] = NULL;
    }
    free(cptr);
    massage_string(needed_tapes_lines[n]);

			/* throw out unneeded levels lower than current */
    if(remove_lower_levels){
      for(cptr = first_nospace(first_space(needed_tapes_lines[n]));
						*cptr; cptr = cptr2){
	i = sscanf(cptr, "%d", &i1);			/* read level */
	if(i < 1)
	  break;

	cptr2 = first_nospace(first_space(cptr));	/* next word pos */

	if(i1 < effective_backup_level){	/* level lower than current */
	  if(*cptr2){				/* if there are more words */
	    memmove(cptr, cptr2, sizeof(UChar) * (strlen(cptr2) + 1));
	    cptr2 = cptr;
	  }				/* copy then over the current word */
	  else{
	    *cptr = '\0';		/* otherwise terminate the string */
	    break;
	  }
	}
      }
    }

    if(remove_previous_equal_level && word_count(needed_tapes_lines[n]) >= 2){
      cptr = needed_tapes_lines[n] + strlen(needed_tapes_lines[n]) - 1;
      cptr = word_start(cptr, 0);
      cptr2 = word_end(cptr, -1);
      while(!isspace(*cptr2) && cptr2 > needed_tapes_lines[n])
	cptr2--;
      if(isspace(*cptr2))
	cptr2++;
      if(!strncmp(cptr, cptr2, i = strlen(cptr)))
	*(cptr2 + i) = '\0';
    }

    if(write_asc_file_safe(needed_tapes_file, needed_tapes_lines, 0))
	errmsg(T_("Error: Could not write file `%s' with the crucial tape numbers"),
				needed_tapes_file);

    all_tape_ranges = get_needed_tapes(needed_tapes_file, server_id);

    if(all_tape_ranges){
	EM__(crucial_tapes_str = str_Uns32Ranges(all_tape_ranges, 0));
	free(all_tape_ranges);
    }
  }
  else{
    errmsg(T_("Error: Evaluating report output to obtain crucial tapes failed"));
  }

  free(stst);

  free_asc_file(lines, 0);
  free_asc_file(needed_tapes_lines, 0);

  if(crucial_tapes_str){
    /* send the crucial tapes to the server using a server message */
    compose_clntcmd(tmpbuf, "", identity, backuphosts[serveridx],
				backupports[serveridx], cartsets[serveridx],
				cryptfile, 0, 0, 0, 0, 0, NULL);
    strcpy(tmpbuf + strlen(tmpbuf), " -M \"" PRECIOUS_TAPES_MESSAGE);
    if( (i = word_count(identity)) > 1)
	strcat(tmpbuf, "\\\"");
    strcat(tmpbuf, identity);
    if(i > 1)
	strcat(tmpbuf, "\\\"");
    sprintf(tmpbuf + strlen(tmpbuf), " %s\"", crucial_tapes_str);
    
    free(crucial_tapes_str);

    ifd = open_to_pipe(tmpbuf, NULL, 1 + 2, &pid, 0);
    close(ifd);

    waitpid_forced(pid, &pst, 0);
    if( (i = WEXITSTATUS(pst)) ){
	errmsg(T_("Error: program writing the minimum restore info to tape returned status %d"),
		(int) i);
    }	
  }


  if(errflp){
    errmsg(T_("Warning: Minor errors occurred during backup. See the logfile `%s' for more details"), logfile);
  }

  do_exit(0);
}

static Int32
set_indexfilename(UChar * idxfile)
{
  ZFREE(indexfile);

  if(idxfile)
    indexfile = strdup(idxfile);

  return(0);
}

static int
open_idxfile_read(Int32 idxnum, int * pid, UChar ** opened_file, Flag quiet)
{
  UChar		*unzipfile = NULL, *unzipcmd, *of = NULL;
  UChar		*idxfile = NULL, *zidxfile = NULL;
  UChar		**unzipfilecont = NULL, numbuf[32];
  int		fd, lpid;

  sprintf(numbuf, "%d", (int) idxnum);
  idxfile = strapp(indexfilepart, numbuf);

  lpid = fd = -1;
  if(!access(idxfile, R_OK)){
    fd = open(idxfile, O_RDONLY);

    if(fd >= 0)
	of = idxfile;
  }
  else{
    zidxfile = strapp(idxfile, COMPRESS_SUFFIX);

    if(access(zidxfile, R_OK)){
      if(!quiet)
	errmsg(T_("Error: No filename logfile found"));
    }
    else{
	unzipcmd = idxuncompresscmd[0] ? idxuncompresscmd : (UChar *) "gunzip";
	unzipfile = hidden_filename(zidxfile);

	if(!access(unzipfile, R_OK)){
	  unzipfilecont = read_asc_file(unzipfile, 0);
	  if(unzipfilecont)
	    if(unzipfilecont[0])
		unzipcmd = unzipfilecont[0];
	}

	fd = open_from_pipe_sigblk(unzipcmd, zidxfile, 1,
				&lpid, &idxproc_blk_sigs);
	if(fd >= 0)
	  of = zidxfile;
    }
  }

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

  set_indexfilename(idxfile);

  if(of && opened_file)
    *opened_file = strdup(of);

  ZFREE(idxfile);
  ZFREE(zidxfile);

  free_asc_file(unzipfilecont, 0);
  ZFREE(unzipfile);

  return(fd);
}

static void
compose_clntcmd(
  UChar		*cbuf,
  UChar		*flags,
  UChar		*id,
  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(id)
    sprintf(cbuf + strlen(cbuf), " -W %s", (char *) 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(StreamerStat * state, UChar * server, Int32 port)
{
  UChar		cbuf[1000], *cptr, *cptr2, *cptr3, *sstr, tmpst;
  Flag		wr = NO, rd = NO, pd = NO, have_statusline = NO;
  Int32		r = 0;
  int		fd, pid, pst, j, k, n;

  SETZERO(*state);
  compose_clntcmd(cbuf, "wv", identity,
			backuphosts[serveridx], backupports[serveridx],
			0, cryptfile, 0, 0, 0, 0, 0, NULL);

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

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

	    forever{
		if( (cptr3 = strchr(cptr, '+')) )
		  *cptr3 = '\0';
		tmpst = (!strcasecmp(cptr, "BUSY") ? STREAMER_BUSY : (
			!strcasecmp(cptr, "READY") ? STREAMER_READY : (
			!strcasecmp(cptr, "UNLOADED") ? STREAMER_UNLOADED : (
			!strcasecmp(cptr, "DEVINUSE") ? STREAMER_DEVINUSE : (
			!strcasecmp(cptr, "UNAVAILABLE") ? STREAMER_UNAVAIL : (
			!strcasecmp(cptr, "CHANGEABLE") ? STREAMER_CHANGEABLE :
				0))))));
		if(tmpst){
		  state->devstatus |= tmpst;
		}
		else{
		  if((wr = !strncasecmp(cptr, "WRITING", 7))
			|| (rd = !strncasecmp(cptr, "READING", 7))
			|| (pd = !strncasecmp(cptr, "PENDING", 7))){
		    j = sscanf(cptr + 7, "=%d", &k);
		    if(j > 0){
			if(wr){
			  state->serverstatus = OPENED_FOR_WRITE;
			  state->numactive += k;
			}
			if(rd){
			  state->serverstatus = OPENED_FOR_READ;
			  state->numactive += k;
			}
			if(pd){
			  state->numpending += k;
			}
		    }
		  }
		  else if(!strcasecmp(cptr, "RAWWRITING")){
		    state->serverstatus = OPENED_FOR_WRITE | OPENED_RAW_ACCESS;
		  }
		  else if(!strcasecmp(cptr, "RAWREADING")){
		    state->serverstatus = OPENED_FOR_READ | OPENED_RAW_ACCESS;
		  }
		}  
		if(!cptr3)
		  break;
		cptr = cptr3 + 1;
	    }
	    have_statusline = YES;
	  }
	  else if(strstr(cptr, "Server-ID:")){
	    strcpy(state->serverid, first_nospace(cptr + strlen("Server-ID:")));
	  }
	  else if(strstr(cptr, "Precious tapes:")){
	    state->needed_tapes = sscan_Uns32Ranges(
			first_nospace(cptr + strlen("Precious tapes:")),
			1, 0xfffffff /* dummy, no open range appears */,
			NULL, NULL);
	  }

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

  if(!state->devstatus)
    state->devstatus = STREAMER_UNKNOWN;

  if(!have_statusline)
    GETOUTR(-1);

  return(0);

 getout:
  ZFREE(state->needed_tapes);

  return(r);
}

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

  if(mode == INSERT_POS_NEWCART){
    next_cart = YES;
    mode = INSERT_POSITION;
 }

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

  if(serverid)
    *serverid = '\0';

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

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

  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);
    pst = WEXITSTATUS(pst);
    if(pst){
	if(pst >= 50 && pst < 100)
      	  return(1);
	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(serverid){
	    if(!strncmp(cptr, "Server-ID:", 10)){
		strcpy(serverid, first_nospace(cptr + 10));
	    }
	  }

	  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
serverid_from_netaddr(
  ServerIdTab	*rid,
  UChar		*servername,
  Int32		serverport,
  UChar		*filen)
{
  ServerIdTab	idtab, *all_ids;
  Int32		r, i, nids;

  r = 1;

  SETZERO(idtab);

  all_ids = get_server_id_entries(filen, &nids);
  if(!all_ids)
    CLEANUPR(-1);

  for(i = 0; i < nids; i++){
    if(serverport == all_ids[i].serverport
			&& !strcmp(servername, all_ids[i].servername)){
	idtab.serverid = strdup(all_ids[i].serverid);
	idtab.servername = strdup(servername);
	idtab.serverport = serverport;

	r = 0;
	break;
    }
  }

 cleanup:
  ZFREE(all_ids);
  if(!r && rid)
    COPYVAL(*rid, idtab);

  return(r);
}

static ServerIdTab *
get_server_id_entries(UChar * filen, Int32 * nids)
{
  UChar		*cptr;
  Int32		i, num_entries, idn;
  ServerIdTab	*server_id_tab = NULL;
  int		i1, n1;
  KeyValuePair	*allentries = NULL;

  allentries = kfile_getall(filen, &num_entries, KFILE_LOCKED);
  if(!allentries)
    return(NULL);

  server_id_tab = NEWP(ServerIdTab, num_entries);
  if(!server_id_tab)
    CLEANUP;

  memset(server_id_tab, 0, num_entries * sizeof(ServerIdTab));

  for(i = idn = 0; i < num_entries; i++){
    cptr = allentries[i].value;
    if(sscanf(cptr, "%d%n", &i1, &n1) < 1)
	continue;

    server_id_tab[idn].servername = strdup(allentries[i].key);

    server_id_tab[idn].serverid = strwordq(cptr + n1, 0);

    if(!server_id_tab[idn].serverid || !server_id_tab[idn].servername)
	GETOUT;

    server_id_tab[idn].serverport = (Int32) i1;

    idn++;
  }

  if(nids)
    *nids = idn;

 cleanup:
  kfile_freeall(allentries, num_entries);

  return(server_id_tab);

 getout:
  if(server_id_tab){
    for(i = 0; i < num_entries; i++){
	ZFREE(server_id_tab[i].serverid);
	ZFREE(server_id_tab[i].servername);
    }
    ZFREE(server_id_tab);
  }

  CLEANUP;
}

static Int32
serverid_from_netaddr_tab(
  UChar		**rid,
  UChar		*servername,
  Int32		serverport,
  ServerIdTab	*tab,
  Int32		tablen)
{
  Int32		i;

  for(i = 0; i < tablen; i++){
    if(serverport == tab[i].serverport
			&& !strcmp(servername, tab[i].servername))
	break;
  }

  if(i < tablen)
    *rid = strdup(tab[i].serverid);
  else
    *rid = NULL;

  return(i < tablen);
}

static Int32
remove_tapes_from_idxes(
  StreamerStat	*ststs,
  Int32		num_ststs,
  UChar		*sid_file,
  Int32		last_idx_num)
{
  Int32		i, n, r = 0, portnum, cartnum, sidtablen;
  UChar		numbuf[32], *newname = NULL, *orgname = NULL, *line = NULL;
  UChar		*idxfile = NULL, *zidxfile = NULL, *unzipfile = NULL, *cptr;
  int		ifd, ofd, ipid, ipst, opid, opst;
  FILE		*ifp = NULL, *ofp = NULL;
  UChar		*servername = NULL, *prevline = NULL, *serverid = NULL;
  Flag		push_line;
  ServerIdTab	*sidtab = NULL;

  for(i = 0; i < num_ststs; i++)
    if(len_Uns32Ranges(ststs[i].deleted_tapes) > 0)
	break;

  if(i >= num_ststs)
    return(0);

  sidtab = get_server_id_entries(sid_file, &sidtablen);
  if(!sidtab)
    return(0);

  n = last_idx_num;
  forever{
    sprintf(numbuf, "%d", (int) n);
    EM__(idxfile = strapp(indexfilepart, numbuf));
    EM__(zidxfile = strapp(idxfile, COMPRESS_SUFFIX));

    if(access(idxfile, F_OK) && access(zidxfile, F_OK))
	break;

    ipid = opid = ipst = opst = ofd = ifd = -1;
    ifd = open_idxfile_read(n, &ipid, &orgname, NO);
    if(ifd < 0)
	break;

    EM__(newname = strapp(idxfile, TMP_SUFFIX));
    unlink(newname);

    if(compresslogfiles){
      ofd = open_to_pipe_sigblk(idxcompresscmd, newname, 1,
				&opid, 0600, &idxproc_blk_sigs);
    }
    if(ofd < 0){
      ofd = open_to_pipe(NULL, newname, 1, NULL, 0600);
      if(ofd < 0){
	fprintf(stderr, T_("Error: Cannot open filename logfile.\n"));
      }
    }

    ifp = fdopen(ifd, "r");
    ofp = fdopen(ofd, "w");

    while(!feof(ifp)){
	ZFREE(line);
	line = fget_alloc_str(ifp);
	if(!line)
	  continue;
	while(chop(line));

	ZFREE(serverid);
	push_line = YES;

	ZFREE(servername);
	portnum = cartnum = -1;
	i = eval_index_line(line, &servername, &portnum, &cartnum,
				NULL, NULL, NULL);
	if(INDEX_FILEENTRY(i)){
	  serverid_from_netaddr_tab(&serverid,
				servername, portnum, sidtab, sidtablen);
	  if(serverid){
	    for(i = 0; i < num_ststs; i++){
	      if(len_Uns32Ranges(ststs[i].deleted_tapes) <= 0)
		continue;

	      if(!strcmp(serverid, ststs[i].serverid)
			&& in_Uns32Ranges(ststs[i].deleted_tapes, cartnum))
		break;
	    }

	    if(i < num_ststs)
		push_line = NO;
	  }
	}

	if(push_line){
	  if(prevline){
	    fprintf(ofp, "%s\n", prevline);
	    ZFREE(prevline);
	  }

	  if(serverid){
	    prevline = line;
	    line = NULL;
	  }
	  else
	    fprintf(ofp, "%s\n", line);
	}
	else{
	  ZFREE(prevline);
	}
    }

    if(prevline){
	fprintf(ofp, "%s\n", prevline);
	ZFREE(prevline);
    }

    fclose(ofp);
    fclose(ifp);

    if(ipid > 0){
	waitpid_forced(ipid, &ipst, 0);
	if( (ipst = WEXITSTATUS(ipst)) ){
	  logmsg(T_("Warning: Command to unprocess index exited with status %d"),
			ipst);
	}
    }
    if(opid > 0){
	waitpid_forced(opid, &opst, 0);
	if( (opst = WEXITSTATUS(opst)) ){
	  logmsg(T_("Warning: Command to process index exited with status %d"),
			opst);
	}
    }

    cptr = (compresslogfiles ? zidxfile : idxfile);
    rename(newname, cptr);
    if(strcmp(cptr, orgname))
	unlink(orgname);

    EM__(unzipfile = hidden_filename(zidxfile));

    ZFREE(newname);
    ZFREE(orgname);
    ZFREE(idxfile);
    ZFREE(zidxfile);

    if(opid > 0){
      ofp = fopen(unzipfile, "w");
      if(ofp){
	fprintf(ofp, "%s\n",
		(idxuncompresscmd[0] ? idxuncompresscmd : (UChar *) "gunzip"));
	fclose(ofp);
      }
      else{
	fprintf(stderr, "Error: Cannot open file `%s'.\n", unzipfile);
      }
    }
    else
	unlink(unzipfile);

    ZFREE(unzipfile);

    n--;
  }

 cleanup:
  ZFREE(idxfile);
  ZFREE(zidxfile);
  ZFREE(unzipfile);
  ZFREE(newname);
  ZFREE(orgname);
  ZFREE(line);
  ZFREE(servername);
  ZFREE(serverid);
  for(i = 0; i < sidtablen; i++){
    ZFREE(sidtab[i].servername);
    ZFREE(sidtab[i].serverid);
  }
  ZFREE(sidtab);

  return(r);
}

static Int32
remove_needed_tapes(UChar * filen, UChar * serverids, Uns32Range * to_remove)
{
  Uns32Range	*tape_ranges = NULL;
  UChar		*cptr, *cptr2, *cptr3, **needed_tapes_lines = NULL;
  UChar		*tmpbuf = NULL, *new_line = NULL, *line;
  Int32		i, j, r = 0;

  if(access(filen, F_OK) || len_Uns32Ranges(to_remove) <= 0)
    return(0);

  needed_tapes_lines = read_asc_file(filen, 0);
  if(!needed_tapes_lines)
    CLEANUPR(-1);

  for(j = 0; ( line = needed_tapes_lines[j] ); j++){
    new_line = strword(line, 0);
    if(!new_line)
	CLEANUPR(-2);

    for(i = 1, cptr = NULL; ; i++){
	tmpbuf = ZRENEWP(tmpbuf, UChar, strlen(line) + 1);
	if(!tmpbuf)
	  CLEANUPR(-20);

	ZFREE(cptr);
	cptr = strword(line, i);
	if(!cptr)
	  break;

	cptr2 = strchr(cptr, '>');
	if(!cptr2)
	  continue;
	cptr2++;
	if(! *cptr2)
	  continue;
	cptr3 = strchr(cptr2, '@');
	if(cptr3){
	  sscanword(cptr3 + 1, tmpbuf);

	  if(strcmp(tmpbuf, serverids)){
	    cptr2 = strchain(new_line, " ", cptr, NULL);
	    if(!cptr2)
		CLEANUPR(-3);

	    free(new_line);
	    new_line = cptr2;

	    continue;
	  }
	}
	tape_ranges = sscan_Uns32Ranges(cptr2, 0, 0, NULL, NULL);
	if(!tape_ranges)
	  CLEANUPR(-5);

	tape_ranges = del_range_from_Uns32Ranges(tape_ranges, to_remove);
	if(!tape_ranges)
	  CLEANUPR(-4);

	if(len_Uns32Ranges(tape_ranges) > 0){
	  strncpy(tmpbuf, cptr, cptr2 - cptr);	/* write level and > */
	  tmpbuf[cptr2 - cptr] = '\0';

	  cptr2 = str_Uns32Ranges(tape_ranges, 0);
	  if(!cptr2)
	    CLEANUPR(-6);

	  tmpbuf = RENEWP(tmpbuf, UChar,
			strlen(tmpbuf) + strlen(cptr2) + strlen(cptr3) + 10);
	  if(!tmpbuf)
	    CLEANUPR(-8);

	  strcat(tmpbuf, cptr2);		/* append new range */
	  free(cptr2);

	  strcat(tmpbuf, cptr3);	/* append @ and server-id */

	  cptr2 = strchain(new_line, " ", tmpbuf, NULL);
	  if(!cptr2)
	    CLEANUPR(-7);

	  free(new_line);
	  new_line = cptr2;
	}

	ZFREE(tape_ranges);
    }

    free(needed_tapes_lines[j]);
    needed_tapes_lines[j] = new_line;
    new_line = NULL;
  }

  r = write_asc_file_safe(filen, needed_tapes_lines, 0);

 cleanup:
  ZFREE(tape_ranges);
  free_asc_file(needed_tapes_lines, 0);
  ZFREE(new_line);
  ZFREE(tmpbuf);

  return(r);
}

static Uns32Range *
get_needed_tapes(UChar * filen, UChar * serverids)
{
  Uns32Range	*all_tape_ranges = NULL, *new_tape_ranges = NULL;
  UChar		*cptr, *cptr2, *cptr3, **needed_tapes_lines = NULL;
  UChar		tmpbuf[1024];
  Int32		i, j;

  all_tape_ranges = empty_Uns32Ranges();
  if(!all_tape_ranges)
    return(NULL);

  needed_tapes_lines = read_asc_file(filen, 0);
  if(!needed_tapes_lines)
    CLEANUP;

  for(j = 0; needed_tapes_lines[j]; j++){
    for(i = 1, cptr = NULL; ; i++){
	ZFREE(cptr);
	cptr = strword(needed_tapes_lines[j], i);
	if(!cptr)
	  break;

	cptr2 = strchr(cptr, '>');
	if(!cptr2)
	  continue;
	cptr2++;
	if(! *cptr2)
	  continue;
	cptr3 = strchr(cptr2, '@');
	if(cptr3){
	  sscanword(cptr3 + 1, tmpbuf);

	  if(strcmp(tmpbuf, serverids))
	    continue;
	}
	EM__(new_tape_ranges = sscan_Uns32Ranges(cptr2, 0, 0, NULL, NULL));

	if(merge_Uns32Ranges(&all_tape_ranges, new_tape_ranges))
	  GETOUT;

	ZFREE(new_tape_ranges);
    }
  }

 cleanup:
  ZFREE(new_tape_ranges);
  free_asc_file(needed_tapes_lines, 0);

  return(all_tape_ranges);

 getout:
  ZFREE(all_tape_ranges);
  CLEANUP;
}

static Int32
add_server_id_entries(UChar * filen, ServerIdTab * ids, Int32 num_ids)
{
  int		fd, i1, n1;
  UChar		**lines = NULL, **cpptr, *cptr;
  UChar		*server_in_line = NULL;
  Int32		num_lines, i, idi, n, r = 0;

  fd = -1;
  if(!access(filen, F_OK)){
    fd = set_wlock_forced(filen);
    if(fd < 0)
	return(-1);

    lines = read_asc_file(filen, 0);
    if(!lines)
	CLEANUPR(-2);
    for(num_lines = 0, cpptr = lines; *cpptr; cpptr++, num_lines++);

    for(idi = 0; idi < num_ids; idi++){
      for(i = 0; i < num_lines; i++){
	n = strlen(lines[i]);
	server_in_line = ZRENEWP(server_in_line, UChar, n);
	
	cptr = sscanwordq(lines[i], server_in_line);
	if(!cptr)
	  continue;
	n = sscanf(cptr, "%d%n", &i1, &n1);
	if(n < 1)
	  continue;

	if((Int32) i1 == ids[idi].serverport
			&& !strcmp(server_in_line, ids[idi].servername))
	  break;
      }

      n = strlen(ids[idi].servername) + strlen(ids[idi].serverid) + 20;

      if(i < num_lines){
	cptr = lines[i] = RENEWP(lines[i], UChar, n);
      }
      else{
	lines = RENEWP(lines, UChar *, num_lines + 2);
	if(!lines)
	  CLEANUPR(-6);

	cptr = lines[num_lines] = NEWP(UChar, n);
	num_lines++;
	lines[num_lines] = NULL;
      }
      if(!cptr)
	CLEANUPR(-5);

      sprintwordq(cptr, ids[idi].servername);
      cptr += strlen(cptr);
      sprintf(cptr, " %d ", (int) ids[idi].serverport);
      cptr += strlen(cptr);
      sprintwordq(cptr, ids[idi].serverid);
    }
  }
  else{
    lines = NEWP(UChar *, num_ids + 1);
    if(!lines)
	CLEANUPR(-3);

    for(i = 0, cpptr = lines; i < num_ids; i++, cpptr++){
	n = strlen(ids[i].servername) + strlen(ids[i].serverid) + 20;
	*cpptr = NEWP(UChar, n);
	if(!(*cpptr))
	  CLEANUPR(-4);

	cptr = *cpptr;
	sprintwordq(cptr, ids[i].servername);
	cptr += strlen(cptr);
	sprintf(cptr, " %d ", (int) ids[i].serverport);
	cptr += strlen(cptr);
	sprintwordq(cptr, ids[i].serverid);
    }
    *cpptr = NULL;
  }

  r = write_asc_file_safe(filen, lines, 0);

 cleanup:
  if(fd >= 0)
    close(fd);
  free_asc_file(lines, 0);
  ZFREE(server_in_line);

  return(r);
}

static Int32
get_args(int argc, char ** argv)
{
  Int32		i;
  UChar		*id_before, *vardir_before;

  id_before = identity;
  vardir_before = vardir;

  switch(mode){
   case MODE_RESTORE:
    i = get_restore_args(argc, argv);
    break;

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

   case MODE_UPDATE_INDEXES:
    i = get_update_indexes_args(argc, argv);
    break;

   case MODE_COPY_TAPE:
    i = get_copytape_args(argc, argv);
    break;

   default:
    i = get_backup_args(argc, argv);
  }

  if(!id_before && identity)
    identity_from_arg = YES;
  if(!vardir_before && vardir)
    vardir_from_arg = YES;

  return(i);
}

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

  if(goptions(-argc, (UChar **) argv, "s:c;s:S;b:d;+b:x;s:h;"
		"s:P;s:C;s:F;s:D;s:I;i:N;f:O;s2:z;i: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;i:Q;s:W;b:G;*",
			&paramfile, &cartsetlist, &detach, &cks,
			&backuphostlist, &backupportlist, &rootdir,
			&filestoskipraw, &dirstoskipraw, &indexfilepart,
			&numindexestostore, &daystostoreindexes,
			&compresscmd, &uncompresscmd,
			&builtin_compr_level, &dont_compress_str,
			&exclude_filename, &logfile, &keep_counter,
			&startinfoprog, &initprog, &exitprog, &clogf, &cbu,
			&cryptfile, &vardir, &verbose, &backup_level,
			&identity, &newcart, &num_unused, &unused_args
			))
    usage(argv[0]);

  if(mode == MODE_FULL_BACKUP && backup_level != MAXINT){
    fprintf(stderr, T_("Error: Flag -Q not allowed with full_backup.\n"));
    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 = YES;
    numparts = 1;
  }

#if 0		/* why did i do this ?????? */
  if(keep_counter)
    numparts = 1;
#endif

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

  return(0);
}

static Int32
get_update_indexes_args(int argc, char ** argv)
{
  static Flag	warned = NO;

  int		i;

  if(curuid){
    if(argc > 1){
      if(!warned){
	fprintf(stderr, "Warning: All supplied arguments are ignored, when not started as the superuser.\n");
	warned = YES;
      }

      return(0);
    }
  }
			/* option -p is handled in the filelist processing */
  i = goptions(-argc, (UChar **) argv,
	"s:c;s:I;s:h;s:P;s2:z;i:Z;b:v;s:V;s:W;s:k",
	&paramfile, &indexfilepart, &backuphostlist, &backupportlist,
	&compresscmd, &uncompresscmd, &builtin_compr_level, &verbose,
	&vardir, &identity, &cryptfile);

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

  return(0);
}

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

			/* 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:W;b:l;b:a;b: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, &identity, &do_listing, &vfy_list_all,
	&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 Int32
get_copytape_args(int argc, char ** argv)
{
  UChar		*host = NULL, *keyfile = NULL, dest = 0, *service = NULL;
  Int32		port = 0, cart = 0, file = 0, i;

  i = goptions(-argc, (UChar **) argv,
		"b:D;s:h;s:P;i:C;i:F;s:k;b:v;s:c;s:l;s:T",
		&dest, &host, &service, &cart, &file, &keyfile,
		&verbose, &paramfile, &logfile, &tmpdir);
  if(i)
    usage(argv[0]);

  if(service){
    port = get_tcp_portnum(service);
    if(port < 0){
	errmsg(T_("Error: Cannot resolve TCP service name `%s'.\n"), service);
	exit(1);
    }
  }

  return(0);
}

static Int32
list_backups(Int32 first_num, Int32 last_num)
{
  Int32		r = 0, i, j, ncall = 0, n, n_lines = 0;
  UChar		*line = NULL, **lines = NULL;
  int		*fds = NULL, *pids = NULL, fd, pid, pst;
  FILE		*fp = NULL;

  for(i = 0; last_num - i >= first_num; i++){
    pid = -1;

    fd = open_idxfile_read(last_num - i, &pid, NULL, YES);
    if(fd < 0)
	break;

    EM__(fds = ZRENEWP(fds, int, i + 1));
    EM__(pids = ZRENEWP(pids, int, i + 1));

    fds[i] = fd;
    pids[i] = pid;
  }

  for(i--; i >= 0; i--){
    fp = fdopen(fds[i], "r");
    if(!fp){
	close(fds[i]);
	if(pids[i] >= 0){
	  kill(pids[i], SIGKILL);	/* no mercy */
	  waitpid_forced(pids[i], &pst, 0);
	}
	fds[i] = pids[i] = -1;
	continue;
    }

    while(!feof(fp)){
	if(fget_realloc_str(fp, &line, &ncall))
	  continue;
	while(chop(line));

	n = eval_index_line(line, NULL, NULL, NULL, NULL, NULL, NULL);
	if(INDEX_COMMENT(n)){
	  if(!read_header_line(line, NULL, NULL, NULL, NULL, NULL)){
	    EM__(lines = ZRENEWP(lines, UChar *, n_lines + 1));
	    EM__(lines[n_lines] = strdup(line));
	    n_lines++;
	  }
	}
    }

    fclose(fp);
    if(pids[i] >= 0){
	waitpid_forced(pids[i], &pst, 0);
	pst = WEXITSTATUS(pst);
	if(pst)
	  errmsg(T_("Warning: Uncompressing index returned bad exit status"));
    }

    if(lines){
	for(j = 0; j < n_lines; j++)
	  fprintf(stdout, "-%d.%d: %s\n", n_lines - 1 - j, i, lines[j]);

	free_asc_file(lines, n_lines);
	lines = NULL;
	n_lines = 0;
    }
  }

 cleanup:
  ZFREE(fds);
  ZFREE(pids);
  ZFREE(line);

  return(r);
}

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

  if(do_listing)
    do_exit(list_backups(vfy_list_all ? 1 :
			(num_prev_run > 0 ? num - num_prev_run : num), num));

  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);
  clientids = NEWP(UChar *, i);
  if(!cnos || !fnos || !servers || !ports || !clientids)
    nomemerrexit();

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

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

  listidx = 0;
  while(!feof(fp)){
    if(fget_realloc_str(fp, &line, &ncall))
	continue;

    server = NULL;
    portn = -1;
    cartn = filen = -1;
    i = eval_index_line(line, &server, &portn, &cartn, &filen, NULL, 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);
    }
    else if(INDEX_COMMENT(i) && !strncmp(first_nospace(line), "##", 2)){
	i = (listidx + num_prev_run) % (num_prev_run + 1);
	if(!clientids[i])
	  EM__(clientids[i] = NEWP(UChar, 132));
	strcpy(clientids[i], identity);
	if(!identity_from_arg)
	  read_header_line(line, NULL, NULL, NULL, NULL, clientids[i]);
    }
  }
  fclose(fp);
  ZFREE(line);

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

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

  compose_clntcmd(tmpbuf, verbose ? "dv" : "d",
		clientids[listidx] ? clientids[listidx] : identity,
		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 Flag	reported = NO;

  Int32		i;
  Flag		have_prev = NO;
  UChar		*dummy = NULL;

  restore_EM = restore_em = 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;"
		"b:L;s:h;s:P;s:I;s:R;s:C;s:V;b:a;c:e;b:f;c:E;s:B;"
		"s:A;b:m;s:T;s:c;s:S;*;s:k;s2:z;b:v;b:q;b:i;s:W;s:F;"
		"i:N;f:O",
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&have_prev, &have_prev, &have_prev, &have_prev, &have_prev,
	&do_counting, &do_listing, &do_raw_listing, &backuphostlist,
	&backupportlist, (curuid ? &dummy : &indexfilepart),
	&restoreroot, &restoreroot, (curuid ? &dummy : &vardir),
	&restore_all, &restore_em, &restore_emlist, &restore_EM,
	&beforestr, &afterstr, &preserve_existing, &restoretapestr,
	(curuid ? &dummy : &paramfile), &cartsetlist,
	&num_restore_paths, &restore_paths, &cryptfile,
	&compresscmd, &uncompresscmd, &verbose, &quiet, &ign_case,
	(curuid ? &dummy : &identity), &formatstr,
	&numindexestoscan, &daystoscanindexes);

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

  if(formatstr && !do_listing)
    fprintf(stderr,
	T_("Warning: The option -F is only allowed in combination with -l, ignored.\n"));

  if(dummy){
    fprintf(stderr,
	T_("Error: The options -c, -I, -W and -V may only be used by the superuser.\n"));
    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 || do_raw_listing)
					&& !(restore_EM || restore_all))
    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, T_("Error: Cannot read date and/or time from \"%s\".\n"),
			afterstr);
	usage(argv[0]);
    }

    if(!reported && !quiet)
	fprintf(stdout, do_listing ?
		T_("Listing files not older than: %s\n") :
		T_("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, T_("Error: Cannot read date and/or time from \"%s\".\n"),
			beforestr);
	usage(argv[0]);
    }
    if(!reported && !quiet)
	fprintf(stdout, do_listing ?
		T_("Listing files not newer than: %s\n") :
		T_("Restoring files not newer than: %s\n"),
			ctime(&time_older));
  }

  if(restoretapestr){
    if(empty_string(restoretapestr)){
	ZFREE(restoretapestr);
    }
    else{
	restoretapes = sscan_Uns32Ranges__(restoretapestr, 1, MAXINT, NULL, NULL);
	EM__(restoretapes);
    }
  }

  reported = YES;

  return(0);
}

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

#define	NEW_CLIENT_START_MARK	((UChar *) 1)

#define	MAXALLOC_FIRST		2097152
#define	MAXALLOC_PROBESTEP	524288
#define	MAXALLOC_STEP		(MAXALLOC_PROBESTEP * 10)

static void
restore(int c, char ** v)
{
  UChar		**cpptr, **cpptr2, **matches = NULL, *line, *cptr, *dirp;
  UChar		**newmatches = NULL, **names = NULL, **newnames = NULL;
  UChar		**auxfnames;		/* uninitialized OK */
  UChar		*auxline, *unzipcmd, *unzipfile = NULL;
  UChar		**unzipfilecont = NULL, new_identity[132];
  Int32		lineno, auxlineno, prevlineno, allocated, num_matches = 0;
  Int32		nc, ns, nf, total_num_matches, num_rest, resterr, sh;
  Int32		matchflags, i, j, n, ncall, auxncall, maxalloc;
  Flag		added, do_client_run, sp, sc, sf, same_loc, only_list;
  Flag		cp_prev, clr_prev, force_new_run, alloc_limit_reached;
  int		fd, pst, pid, fileuid, auxfd, auxpst, auxpid;
  FILE		*fp, *auxfp;
  struct stat	statb;
  time_t	newtim, filemtime;
  DataLocator	act_dataloc, prev_dataloc;
  UChar		fmtpat[3];

  if(numindexestoscan >= 0 && daystoscanindexes >= 0.0)
    errmsg(T_("Warning: Both DaysToScanIndexes and NumIndexesToScan configured. Ignoring the shorter duration"));

  if(daystoscanindexes >= 0.0){		/* set numindexestoscan from it */
    time_t	read_time, cmp_time;

    cmp_time = time(NULL) - (time_t)(daystoscanindexes * 86400.0);

    for(i = num - 1; ; i--){
      read_time = (time_t) 0;

      sprintf(tmpbuf, "%d:", (int) i);
      cptr = kfile_get(index_ages_file, tmpbuf, KFILE_LOCKED);

      if(cptr){
	read_time = strint2time(cptr);
	free(cptr);
      }
      else{
	break;		/* nothing there, cannot go back further */
      }

      if(read_time < cmp_time)
	break;
    }

    i = num - i;
    if(i > numindexestoscan)
	numindexestoscan = i;
  }

  if(formatstr){
    for(; (cptr = strstr(formatstr, "%%")); *(cptr + 1) = '\"');
    for(; (cptr = strstr(formatstr, "\\%")); *(cptr + 1) = '\"', *cptr = '%');
    EM__(fmt_repls = NEWP(ReplSpec, 1));
    num_fmt_repls = 1;
    fmt_repls[0].token = "%\"";
    fmt_repls[0].repl = strdup("%");

    strcpy(fmtpat, "%%");
    for(cptr = FMT_REPL_CHARS; *cptr; cptr++){
	fmtpat[1] = *cptr;
	if(strstr(formatstr, fmtpat)){
	  EM__(fmt_repls = RENEWP(fmt_repls, ReplSpec, num_fmt_repls + 1));
	  SETZERO(fmt_repls[num_fmt_repls]);
	  fmt_repls[num_fmt_repls].token = strdup(fmtpat);
	  num_fmt_repls++;
	}
    }
  }

  if(restoreroot){
    i = 0;

    if(curuid){
	dirp = strdup(restoreroot);
	cleanpath(dirp);
	forever{
	  if(!strcmp(dirp, "")){
	    free(dirp);
	    dirp = strdup(".");
	  }

	  i = stat(dirp, &statb);
	  if(!i){
	    if(!IS_DIRECTORY(statb)){
		errmsg(T_("Error: `%s' is not a directory"), dirp);
		do_exit(45);
	    }
	    i = access(dirp, X_OK);
	    if(i){
		errmsg(T_("Error: Cannot read directory \"%s\". No execute permission"),
			dirp);
		do_exit(20);
	    }
	    if(statb.st_uid != curuid){
		errmsg(T_("Error: The directory \"%s\" is not owned by you"),
			dirp);

		do_exit(22);
	    }

	    break;
	  }

	  if(FN_ISROOTDIR(dirp) || !strcmp(dirp, "."))
	    break;
	  cptr = FN_LASTDIRDELIM(dirp);
	  if(cptr == dirp)
	    cptr++;
	  if(!cptr)
	    cptr = dirp;

	  *cptr = '\0';
	}

	free(dirp);
    }

    i = make_dirpath(restoreroot, 0700, curuid, curgid);
    if(i < 0){
	errmsg(T_("Error: Cannot make directory `%s'"), restoreroot);
	do_exit(44);
    }
    if(i > 0){
	errmsg(T_("Warning: Cannot change owner/group of `%s'. Restore might fail"),
			restoreroot);
    }

    rootdir = restoreroot;
  }

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

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

  only_list = do_listing || do_counting;

  if(restore_all && only_list){
    EM__(restore_paths = NEWP(UChar *, 2));
    EM__(restore_paths[0] = strdup("*"));
    restore_paths[1] = NULL;
    num_restore_paths = 1;
    restore_all = NO;
  }

  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{
      n = strlen(restore_paths[i]);
      if(n > 0)
	cptr = strchain(restore_paths[i][0] == '*' ? "" : "*",
			restore_paths[i],
			restore_paths[i][n - 1] == '*' ? "" : "*",
			NULL);
      else
	cptr = strdup("*");
      free(restore_paths[i]);
      restore_paths[i] = cptr;
    }

    cleanpath(restore_paths[i]);
  }

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

  indexfiles = NEWP(UChar *, 3);
  zindexfiles = NEWP(UChar *, 3);
  if(!indexfiles || !zindexfiles)
    nomemerrexit();
  memset(indexfiles, 0, 3 * sizeof(UChar *));
  memset(zindexfiles, 0, 3 * 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_pint_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);
  }
  if(have_timerange || restoretapes){
    num_indexfiles = 0;

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

      if(numindexestoscan >= 0 && num_indexfiles >= numindexestoscan)
	break;

      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(T_("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);		/* will exit */

  matchflags = GFNM_PATHNAME | GFNM_NOESCAPE;
  if(ign_case)
    matchflags |= GFNM_CASEFOLD;

  line = auxline = NULL;
  ncall = auxncall = 0;
  allocated = 0;
  current_bu_time = 0;
  prev_bu_time = 0;

  SETZERO(act_dataloc);
  SETZERO(prev_dataloc);
  ns = nc = nf = total_num_matches = resterr
				= lineno = auxlineno = prevlineno = 0;

  unzipcmd = idxuncompresscmd[0] ? idxuncompresscmd : (UChar*) "gunzip";

  auxfp = NULL;		/* to read ahead to get the next timestamp entry */
  auxpid = -1;
  if(have_timerange){
    auxfnames = indexfiles;
    auxfd = open(*auxfnames, O_RDONLY);
    if(auxfd < 0){
	i = auxfnames - indexfiles;

	cptr = unzipcmd;

	unzipfile = hidden_filename(zindexfiles[i]);
	if(!access(unzipfile, R_OK)){
	  unzipfilecont = read_asc_file(unzipfile, 0);
	  if(unzipfilecont)
	    if(unzipfilecont[0])
		cptr = unzipfilecont[0];
	}

	auxfd = open_from_pipe_sigblk(cptr, zindexfiles[i], 1,
				&auxpid, &idxproc_blk_sigs);
	if(auxfd >= 0)
	  auxfp = fdopen(auxfd, "r");

	ZFREE(unzipfile);
	free_asc_file(unzipfilecont, 0);
	unzipfilecont = NULL;
    }
  }

  maxalloc = MAXALLOC_FIRST;
  alloc_limit_reached = NO;

  for(cpptr = indexfiles; *cpptr; cpptr++){
    have_mtime_idx = NO;
    force_new_run = NO;
    pid = -1;
    fd = open(*cpptr, O_RDONLY);
    if(fd < 0){
	i = cpptr - indexfiles;

	cptr = unzipcmd;

	unzipfile = hidden_filename(zindexfiles[i]);
	if(!access(unzipfile, R_OK)){
	  unzipfilecont = read_asc_file(unzipfile, 0);
	  if(unzipfilecont)
	    if(unzipfilecont[0])
		cptr = unzipfilecont[0];
	}

	fd = open_from_pipe_sigblk(cptr, zindexfiles[i], 1,
					&pid, &idxproc_blk_sigs);

	ZFREE(unzipfile);
	free_asc_file(unzipfilecont, 0);
	unzipfilecont = NULL;

	if(fd < 0)
	  continue;
    }
    fp = fdopen(fd, "r");
    if(!fp){
	errmsg(T_("Error: Cannot read file \"%s\""), *cpptr);
	continue;
    }
    while(!feof(fp)){
      if(fget_realloc_str(fp, &line, &ncall))
	continue;
      lineno++;

      if(have_timerange){	/* have to read ahead from second stream */
	if(!auxfp && !current_bu_time)
	  current_bu_time = time(NULL);

	if(auxfp && lineno >= auxlineno){
	  current_bu_time = 0;
	  while(!current_bu_time && auxfp){
	    if(fget_realloc_str(auxfp, &auxline, &auxncall)){
		fclose(auxfp);
		if(auxpid > 0){
		  waitpid_forced(auxpid, &auxpst, 0);

		  if(WEXITSTATUS(auxpst)){
		    errmsg(T_("Warning: Uncompressing \"%s\" returned bad exit status"),
			*(auxfnames + 3));
		  }
		  auxpid = -1;
		}
		auxfp = NULL;

		auxfnames++;
		if(! *auxfnames){
		  current_bu_time = time(NULL);
		  break;
		}

		auxfd = open(*auxfnames, O_RDONLY);
		if(auxfd < 0){
		  auxfd = open_from_pipe_sigblk(unzipcmd,
			zindexfiles[auxfnames - indexfiles], 1,
				&auxpid, &idxproc_blk_sigs);
		  if(auxfd >= 0)
		    auxfp = fdopen(auxfd, "r");
		}

	 	continue;
	    }

	    auxlineno++;
	    if(auxlineno <= lineno)
		continue;

	    if(!strncmp(cptr = first_nospace(auxline), "##", 2)){
		if(!read_header_line(cptr, NULL, &newtim, NULL, NULL, NULL)){
		  current_bu_time = newtim;
		}
	    }
	  }	/* while don't have timestamp and stuff to read */
	}	/* if have stuff to read and not yet read enough lines */
      }		/* if timestamps have to be evaluated */

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

      if(!strncmp(cptr = first_nospace(line), "##", 2)){
		/* if the client identifier is not given on the command */
			/* line, the one in the index is used, if found */
	strcpy(new_identity, "");
	if(!read_header_line(cptr, NULL, &newtim, NULL, NULL, new_identity)){
	  prev_bu_time = newtim;

	  if(matches)
	    force_new_run = YES;		/* force a new client start */
	}

	if(identity_from_arg)
	  strcpy(new_identity, "");

	if(strcmp(new_identity, "")){
	  ZFREE(identity);
	  identity = strdup(new_identity);
	}

	continue;
      }

      fileuid = - MAXINT;
      filemtime = (time_t) -1;

      ZFREE(act_dataloc.server);
      SETZERO(act_dataloc);
      act_dataloc.port = -1;
      i = eval_index_line(line, &act_dataloc.server, &act_dataloc.port,
		&act_dataloc.cart, &act_dataloc.file, &fileuid, &filemtime);

      if(act_dataloc.port == -1)
	act_dataloc.port = DEFAULT_PORT;
      if(!act_dataloc.server)
	act_dataloc.server = strdup(backuphosts[0]);

      if(!INDEX_FILEENTRY(i))
	continue;
      if(!line[i])
	continue;

      if(curuid && curuid != fileuid)
	continue;

      if(restoretapes)
	if(!in_Uns32Ranges(restoretapes, act_dataloc.cart))
	  continue;

      if(have_timerange){
	if(filemtime != (time_t) -1){
	  if((time_newer > 0 && filemtime < time_newer)
		|| (time_older > 0 && filemtime > time_older))
	    continue;
	}
	else{
	  if((time_newer > 0 && current_bu_time < time_newer)
		|| (time_older > 0 && prev_bu_time > time_older))
	    continue;
	}
      }

      cptr = line + i;

      for(i = 0; i < num_restore_paths; i++){
	if(!fn_match(restore_paths[i], cptr, matchflags)){
	  if(have_timerange && filemtime != (time_t) -1)
	    have_mtime_idx = YES;

	  total_num_matches++;

	  if(!do_raw_listing){
	    added = do_client_run = cp_prev = clr_prev = NO;

	    if(!nf)
		nf = ns = nc = 1;

	    if(!prev_dataloc.cart){
		COPYVAL(prev_dataloc, act_dataloc);
		prev_dataloc.server = strdup(act_dataloc.server);
		force_new_run = NO;
	    }
	    if(!prev_dataloc.efile)
		prev_dataloc.efile = act_dataloc.file;

	    sh = same_host(act_dataloc.server, prev_dataloc.server);
	    sp = (act_dataloc.port == prev_dataloc.port);
	    sc = (act_dataloc.cart == prev_dataloc.cart);
#if 0		/* why did i do this ??? anyone any idea ??? */
	    sf = (act_dataloc.file == prev_dataloc.file && !current_bu_time)
		|| ((act_dataloc.file == prev_dataloc.file
				|| act_dataloc.file == prev_dataloc.file + 1
					|| prevlineno + 1 == lineno)
				&& current_bu_time);
#else
	    sf = (act_dataloc.file == prev_dataloc.file
				|| act_dataloc.file == prev_dataloc.file + 1
					|| prevlineno + 1 == lineno);
#endif
	    if(sh <= 0 || !sp){
		ns++;
		nc++;
		nf++;
	    }
	    else if(!sc){
		nc++;
		nf++;
	    }
	    else if(act_dataloc.file != prev_dataloc.file){
		nf++;
	    }

	    if(only_list){
		cp_prev = YES;

		if(do_listing){
		  if(fmt_repls){
		    dirp = cptr;
		    cptr = repl_fmts(formatstr, fmt_repls, num_fmt_repls,
					line, prev_bu_time);
		    fprintf(stdout, "%s", cptr);
		    if(cptr != dirp)
			free(cptr);
		  }
		  else
		    fprintf(stdout, "%s\n", cptr);
		}
	    }
	    else{
	      same_loc = sc && sp && (sh > 0) && sf;

	      j = num_matches + 1 + ((!same_loc || force_new_run) ? 1 : 0);
	      newmatches = ZRENEWP(matches, UChar *, j);
	      newnames = ZRENEWP(names, UChar *, j);
	      if(newmatches)
		matches = newmatches;
	      if(newnames)
		names = newnames;
	      if(!newmatches || !newnames){
		if(num_matches < 10)
		  nomemerrexit();

		do_client_run = YES;
	      }

	      if(do_client_run){
		num_rest = total_num_matches - 1;

		fprintf(stdout, T_("Found %d matches on %d servers, %d tapes, %d"
			" files up to now.\n This is not the last pack, please"
			" be patient.\n"), (int) num_rest,
			(int) ns, (int) nc, (int) nf);

		j = num_matches;
		n = write_to_restore(matches, names, &j);
		num_rest -= (num_matches - j);
		if(!n){			/* write_to_restore frees matches */
		  if(num_matches > j){
		    fprintf(stdout,
			T_("Detected %d duplicate entries, only the newest ones are restored.\n"),
						(int) (num_matches - j));
		  }
		  fprintf(stdout, T_("Successfully restored %d filesystem entries"
			", %d totally from %d servers, %d tapes and %d files, "
				"continuing.\n"), (int) num_matches,
			(int) num_rest, (int) ns, (int) nc, (int) nf);
		}
		else{
		  errmsg(T_("Errors occurred during restore of the previous %d"
			" files. See above for details."), (int) num_matches);
		  resterr++;
		}

		matches = NEWP(UChar *, 1);
		names = NEWP(UChar *, 1);
		if(!matches || !names)
		  nomemerrexit();
		force_new_run = NO;

	 	num_matches = allocated = 0;
		prev_dataloc.efile = act_dataloc.file;

		maxalloc = MAXALLOC_FIRST;
		alloc_limit_reached = NO;
	      }
	      else{
		if(!same_loc || force_new_run)
		  matches[num_matches++] = NEW_CLIENT_START_MARK;
		force_new_run = NO;
	      }
	      cp_prev = YES;

	      matches[num_matches] = strdup(line);
	      names[num_matches] = matches[num_matches] + (cptr - line);
	      if(matches[num_matches]){
		num_matches++;
		added = YES;
		allocated += strlen(line) + 1;
	      }
	      if(allocated > maxalloc){	/* try to get 5 more M of real mem */
		j = bytes_free_real_mem_pag(MAXALLOC_PROBESTEP, MAXALLOC_STEP);
		if(j < MAXALLOC_STEP)
		  alloc_limit_reached = YES;
		else
		  maxalloc += j / 2;	/* if ok: allow to use the half */
	      }
	      if((allocated > maxalloc && alloc_limit_reached) || !added){
		num_rest = total_num_matches - (added ? 0 : 1);

		fprintf(stdout, T_("Found %d matches on %d servers, %d tapes, %d"
			" files up to now.\n This is not the last pack, please"
			" be patient.\n"), (int) num_rest,
			(int) ns, (int) nc, (int) nf);

		j = num_matches;
		n = write_to_restore(matches, names, &j);
		num_rest -= (num_matches - j);
		if(!n){			/* write_to_restore frees matches */
		  if(num_matches > j){
		    fprintf(stdout,
			T_("Detected %d duplicate entries, only the newest ones are restored.\n"),
						(int) (num_matches - j));
		  }
		  fprintf(stdout, T_("Successfully restored %d filesystem entries"
			", %d totally from %d servers, %d tapes and %d files, "
				"continuing.\n"), (int) num_matches,
			(int) num_rest, (int) ns, (int) nc, (int) nf);
		}
		else{
		  errmsg(T_("Errors occurred during restore of the previous %d"
			" files. See above for details."), (int) num_matches);
		  resterr++;
		}

		matches = NEWP(UChar *, 1);
		names = NEWP(UChar *, 1);
		if(!matches || !names)
		  nomemerrexit();
		force_new_run = NO;

		if(!added){
		  EM__(matches[0] = strdup(line));
		  names[0] = matches[0] + (cptr - line);
		}
		num_matches = added ? 0 : 1;
		allocated = added ? 0 : strlen(line) + 1;
		clr_prev = added;
		cp_prev = ! added;
		prev_dataloc.efile = added ? 0 : act_dataloc.file;

		maxalloc = MAXALLOC_FIRST;
		alloc_limit_reached = NO;
	      }
	    }

	    if(cp_prev || clr_prev)
		ZFREE(prev_dataloc.server);
	    if(clr_prev)
		SETZERO(prev_dataloc);
	    if(cp_prev){
		act_dataloc.efile = prev_dataloc.efile;	/* to keep it */
		COPYVAL(prev_dataloc, act_dataloc);
		act_dataloc.server = NULL;
	    }
	    prev_dataloc.file = act_dataloc.file;
	  }
	  else{			/* do_raw_listing */
	    fprintf(stdout, "%s\n", cptr);
	  }
		/* if successive lines match, don't start new collect */
	  prevlineno = lineno;

	  break;
	}
      }
    }

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

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

  ZFREE(line);
  ZFREE(auxline);
  ncall = auxncall = 0;

  if(!do_raw_listing){
    if(do_counting || !do_listing){
	if(total_num_matches > 0)
	  fprintf(stdout, T_("Found %d matches on %d servers, %d tapes, %d files.\n"),
		(int) total_num_matches, (int) ns, (int) nc, (int) nf);
	else
	  fprintf(stdout, T_("No matches found.\n"));
    }

    if(!only_list){
      if(total_num_matches > 0){
	j = num_matches;
	n = write_to_restore(matches, names, &j);
	total_num_matches -= (num_matches - j);
	if(!n){
	  if(num_matches > j){
	    fprintf(stdout,
		T_("Detected %d duplicate entries, only the newest ones are restored.\n"),
					(int) (num_matches - j));
	  }
	  fprintf(stdout, T_("Successfully restored %d filesystem entries"
		" from %d servers, %d tapes and %d files.\n"),
		(int) total_num_matches, (int) ns, (int) nc, (int) nf);
	}
	else{
	  errmsg(T_("Errors occurred during restore of the previous %d matches."
		" See previous output for details"), (int) num_matches);
	  resterr++;
	}
      }
    }
  }

  ZFREE(act_dataloc.server);
  ZFREE(prev_dataloc.server);

  if(resterr)
    errmsg(T_("Errors occurred during restore. See previous output for details"));

  do_exit(resterr ? 3 : 0);
}

static UChar *
repl_fmts(
  UChar		*fmt,
  ReplSpec	*repls,
  Int32		num_repls,
  UChar		*str,
  time_t	butim)
{
  Int32		i, n, port, cart, file;
  int		uid;
  time_t	mtim;
  UChar		*server = NULL, *newstr;
  UChar		portstr[16], cartstr[16], filestr[16], uidstr[16];
  UChar		mtimestr[32], Mtimestr[32], btimestr[32], Btimestr[32];
  UChar		loginstr[32];
  struct passwd	*uent;

  port = cart = file = uid = -1;
  mtim = (time_t) -1;

  i = eval_index_line(str, &server, &port, &cart, &file, &uid, &mtim);
  if(i <= 0)
    return(str);

  newstr = strdup(fmt);
  if(!newstr)
    return(str);
  repl_esc_seq(newstr, '\\');

  if(port != -1)
    sprintf(portstr, "%d", (int) port);
  else
    strcpy(portstr, "???");
  if(cart != -1)
    sprintf(cartstr, "%d", (int) cart);
  else
    strcpy(cartstr, "???");
  if(file != -1)
    sprintf(filestr, "%d", (int) file);
  else
    strcpy(filestr, "???");
  if(uid != -1){
    sprintf(uidstr, "%d", (int) uid);
    uent = getpwuid(uid);
    if(uent)
	sprintf(loginstr, "%s", uent->pw_name);
    else
	sprintf(loginstr, "%d", (int) uid);
  }
  else{
    strcpy(uidstr, "???");
    strcpy(loginstr, uidstr);
  }
  if(mtim != (time_t) -1){
    time_t_to_intstr(mtim, mtimestr);
    strcpy(Mtimestr, ctime(&mtim));
    massage_string(Mtimestr);
  }
  else{
    strcpy(mtimestr, "???");
    strcpy(Mtimestr, mtimestr);
  }
  if(butim != (time_t) -1){
    time_t_to_intstr(butim, btimestr);
    strcpy(Btimestr, ctime(&butim));
    massage_string(Btimestr);
  }
  else{
    strcpy(btimestr, "???");
    strcpy(Btimestr, Btimestr);
  }

  for(n = 0; n < num_repls; n++){
    switch(repls[n].token[1]){
      case 'n':
	repls[n].repl = str + i;
	break;
      case 'b':
	repls[n].repl = FN_BASENAME(str + i);
	break;
      case 'f':
	repls[n].repl = filestr;
	break;
      case 'p':
	repls[n].repl = portstr;
	break;
      case 'h':
	repls[n].repl = server ? server : (UChar *) "???";
	break;
      case 'c':
	repls[n].repl = cartstr;
	break;
      case 'o':
	repls[n].repl = uidstr;
	break;
      case 'O':
	repls[n].repl = loginstr;
	break;
      case 'm':
	repls[n].repl = mtimestr;
	break;
      case 'M':
	repls[n].repl = Mtimestr;
	break;
      case 't':
	repls[n].repl = btimestr;
	break;
      case 'T':
	repls[n].repl = Btimestr;
	break;
    }
  }

  i = repl_substrings(&newstr, repls, num_repls);
  ZFREE(server);

  return(newstr);
}

static Int32
write_to_restore(
  UChar		**matches,
  UChar		**names,
  Int32		*n_matches)
{
  int		fd, pid, pst;
  Int32		i, j, ret = 0, lineno, portno, cartno, fileno, efileno;
  Int32		num_matches;
  UChar		**cpptr, *server = NULL, flagstr[16];

  num_matches = *n_matches;

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

  if(!have_timerange || have_mtime_idx){	/* eliminate duplicates */
    for(i = num_matches - 1; i > 0; i--){
      for(j = i - 1; j >= 0; j--){
	if(matches[i] != NEW_CLIENT_START_MARK
			&& matches[j] != NEW_CLIENT_START_MARK){
	  if(!strcmp(names[i], names[j])){
	    num_matches--;
	    free(matches[j]);
	    memmove(matches + j, matches + j + 1,
				(num_matches - j) * sizeof(matches[0]));
	    memmove(names + j, names + j + 1,
				(num_matches - j) * sizeof(names[0]));
	    i--;
	  }
	}
      }
    }
  }

  *n_matches = num_matches;

  fd = -1;
  lineno = 0;

  forever{
    while(lineno < num_matches){
      if(matches[lineno] != NEW_CLIENT_START_MARK)
	break;
      lineno++;
    }

    if(lineno >= num_matches)
	break;

    if(fd < 0){
	ZFREE(server);
	i = eval_index_line(matches[lineno],
			&server, &portno, &cartno, &fileno, NULL, NULL);
	if(!INDEX_FILEENTRY(i)){
	  errmsg(T_("Error: no fileentry given to write_to_server:\n> %s\n"),
				matches[lineno]);
	  free(matches[lineno]);
	  continue;
	}

	strcpy(flagstr, "xvg");
	if(!preserve_existing)
	  strcat(flagstr, "u");
	if(chk_perms)
	  strcat(flagstr, "Z");
	if(curuid){
	  sprintf(flagstr + strlen(flagstr), " -o %d:%d",
					(int) curuid, (int) curgid);
	  for(i = 0; i < curngids; i++)
	    sprintf(flagstr + strlen(flagstr), "%c%d",
				(i ? ',' : ':'), (int) curgids[i]);
	}
	compose_clntcmd(tmpbuf, flagstr,
		identity, server, portno, 0, cryptfile, cartno, fileno,
		time_older, time_newer, 0 /* set above */, restoreroot);

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

	SETZERO(scandir_args);
	scandir_args.fd = fd;

	for(j = lineno + 1; j < num_matches; j++)
	  if(matches[j] == NEW_CLIENT_START_MARK)
	    break;
	j--;
	eval_index_line(matches[j], NULL, NULL, NULL, &efileno, NULL, NULL);

	if(fileno == efileno)
	  fprintf(stdout,
		T_("Restoring files from server %s, port %d, cartridge %d, file %d\n"),
				server, (int) portno, (int) cartno, (int) fileno);
	else
	  fprintf(stdout,
		T_("Restoring files from server %s, port %d, cartridge %d, files %d - %d\n"),
			server, (int) portno, (int) cartno,
				(int) fileno, (int) efileno);

	fflush(stdout);
    }

    for(cpptr = names + lineno; fd >= 0; lineno++, cpptr++){
	if(matches[lineno] == NEW_CLIENT_START_MARK || lineno >= num_matches){
	  close(fd);
	  waitpid_forced(pid, &pst, 0);

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

	  if(fileno == efileno)
	    fprintf(stdout,
		T_("Done with server %s, port %d, cartridge %d, file %d\n"),
				server, (int) portno, (int) cartno, (int) fileno);
	  else
	    fprintf(stdout,
		T_("Done with server %s, port %d, cartridge %d, files %d - %d\n"),
			server, (int) portno, (int) cartno,
				(int) fileno, (int) efileno);

	  fd = -1;
	}
	else{
	  write_forced(fd, *cpptr, strlen(*cpptr));
	  write_forced(fd, "\n", 1);

		/* write_filename(cptr, (void *) (& scandir_args), NULL); */
	  free(matches[lineno]);
	}
    }
  }

  fflush(stdout);

 cleanup:
  free(names);
  free(matches);
  ZFREE(server);

  return(ret);
}

static void
restoreall(int c, char ** v)
{
  UChar		**cpptr, *cptr, *line = NULL, *prevlast = NULL;
  Flag		server_changed, found;
  UChar		*filename, *line2 = NULL, *server = NULL, *oldserver = NULL;
  UChar		*unzipfile = NULL, **unzipfilecont = NULL, *unzipcmd;
  FILE		*fp, *cfp;
  int		fd, pid, pst, n, cpid, cfd;
  Int32		i, cartno, fileno, oldcart, oldfile, portno, oldport;
  Int32		ncall = 0, ncall2 = 0;
  Int32		pllen;	/* uninitialized ok */

  oldcart = oldfile = oldport = -1;

  for(cpptr = indexfiles; *cpptr; cpptr++){
    pid = -1;
    fd = open(*cpptr, O_RDONLY);
    if(fd < 0){
	unzipcmd = idxuncompresscmd[0] ? idxuncompresscmd : (UChar *) "gunzip";
	i = cpptr - indexfiles;

	unzipfile = hidden_filename(zindexfiles[i]);
	if(!access(unzipfile, R_OK)){
	  unzipfilecont = read_asc_file(unzipfile, 0);
	  if(unzipfilecont)
	    if(unzipfilecont[0])
		unzipcmd = unzipfilecont[0];
	}

	fd = open_from_pipe_sigblk(unzipcmd, zindexfiles[i], 1,
					&pid, &idxproc_blk_sigs);

	ZFREE(unzipfile);
	free_asc_file(unzipfilecont, 0);
	unzipfilecont = NULL;

	if(fd < 0)
	  continue;
    }
    fp = fdopen(fd, "r");
    if(!fp){
	errmsg(T_("Error: Cannot read file \"%s\""), *cpptr);
	continue;
    }
    found = NO;
    while(!feof(fp)){	/* search input logfile lines */
      if(fget_realloc_str(fp, &line, &ncall))
	continue;

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

      server_changed = (server && !oldserver) || (!server && oldserver)
		|| (server && oldserver && same_host(server, oldserver) <= 0);

	/* 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;
      }

      chop(line);
      filename = line + n;

      if(!prevlast || found){	/* if found or first line at all: restore */
	compose_clntcmd(tmpbuf, preserve_existing ? "xvanl" : "xvanlu",
			identity, oldserver, portno, 0,
			cryptfile, cartno, fileno, time_older, time_newer,
					curuid, restoreroot);

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

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

	fprintf(stdout,
		T_("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(fget_realloc_str(cfp, &line2, &ncall2))
	    continue;

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

	  n = eval_index_line(line2, NULL, 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){
	    prevlast = strdup(cptr);
	    pllen = strlen(prevlast);
	  }
	  else{
	    if((n = strlen(cptr)) > pllen){
		EM__(prevlast = RENEWP(prevlast, UChar, n + 1));
		pllen = n;
	    }
	    strcpy(prevlast, cptr);
	  }
	}

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

	found = NO;
      }

      if(!prevlast)
	break;

      if(!strcmp(prevlast, filename)){
	found = YES;
      }
      else{
	if(restoreroot){	/* must be more tolerant, afclient might */
	  mkrelpath(filename);	/* output a relative path, what makes sense */

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

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

    ZFREE(prevlast);
  }

  ZFREE(line);
  ZFREE(line2);

  do_exit(0);
}

Int32
read_header_line(
  UChar		*line,
  Int32		*mode,
  time_t	*starttime,
  Int32		*bunum,
  UChar		*hostname,
  UChar		*clientid)
{
  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){
    line = first_nospace(first_space(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++){
	line = first_nospace(first_space(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);
    if(line){
	line += strlen(cptr);
	sscanf(line, "%d", &read_num);
	if(bunum)
	  *bunum = (Int32) read_num;
    }

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

    cptr = "client id:";
    if(line)
	line = strstr(line, cptr);
    if(line){
	line = first_nospace(line + strlen(cptr));
	if(clientid)
	  line = sscanwordq(line, clientid);
    }
    else{
	if(clientid)
	  strcpy(clientid, hostname ? hostname : (UChar *) "???");
    }

    return(0);
  }

  return(1);
}

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 = (same_host(s1, s2) > 0 ? 0 : 1);
  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);
}

#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
		&& same_host(buf_entries[i].server, server) > 0){
	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, NULL);
  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;

  StreamerStat	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
		&& same_host(buf_entries[i].server, server) > 0){
	if(buf_entries[i].mode < 0)
	  continue;

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

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

  ZFREE(actstate.needed_tapes);

  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.devstatus;

  num_buf_entries++;

  return(0);
}

static Int32
sendrcv(
  int		fromfd,
  UChar		*frombuf,
  Int32		numfrom,
  int		tofd,
  UChar		*tobuf,
  Int32		numto)
{
  Int32		i, j;

  if(numto > 0){
    i = (write_forced(tofd, tobuf, numto) != numto);
    if(i)
      return(- errno);
  }
  if(numfrom > 0){
    j = (read_forced(fromfd, frombuf, numfrom) != numfrom);
    if(j)
      return(- errno);
  }

  return(0);
}

static Int32
simple_cmd(
  Int32		cmd,
  int		fromfd,
  int		tofd)
{
  UChar		cmdb, res;
  Int32		i;

  cmdb = (UChar) cmd;
  i = sendrcv(fromfd, &res, 1, tofd, &cmdb, 1);
  return(i ? i : (Int32) res);
}

static void
restoreem(int c, char ** v)
{
  UChar		**lines = NULL, *line = NULL, *zindexfile, **servers = NULL;
  UChar		*cptr, *cptr2, *mininfoline = NULL, *unzipfile;
  Flag		noservavail, success, havelastfull;
  UChar		*clientname = NULL, *servername, *filename, *rfilename;
  Int32		num_lines = 0, serverport, cartno, fileno;
  int		i, j, k, l, n, act_bunum = -1, nump, isp, bunum;
  FILE		*fp;
  int		fd, pid, opid, pst, ofd, pp[2];
  Int32		num_backups, *carts, *files, *ports, *bunums, stopidx;
  Int32		num_search_entries = 0, idx, ncall = 0;
  Int32		actinscart, inscart, num_to_search;
  Int32		latest_bupart, num_latest_buparts, startidx;	/* uninitialized OK */
  Int32		earliest_bunum, numcarts = 1, actcart, f;
  Int32		latest_bunum_cart, earliest_bunum_cart, latest_bunum;
  UChar		min_restore_info[1000];
  BuTapeEntry	*search_entries = NULL, *tmpentries;
  StreamerStat	actstate;

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

  if(restore_EM){		/* emergency restore without min info */
    time_t	latest_timestamp;

    min_restore_info[0] = '\0';

    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(T_("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;
	  EM__(search_entries = ZRENEWP(search_entries, BuTapeEntry,
					num_search_entries + k - j + 1));

	  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{
		EM__(cptr = strdup(cptr));

		if(cptr2)
		  l = get_tcp_portnum(cptr2 + 1);
	    }
	  }
	  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];

	EM__(search_entries = ZRENEWP(search_entries, BuTapeEntry,
				num_search_entries + num_prev_carts + 1));

	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)
		l = get_tcp_portnum(cptr2 + 1);
	  }
	}

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

	if(num_prev_carts + 1 > numcarts){
	  errmsg(T_("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){
	  EM__(cptr = search_entries[num_search_entries].server =
					strdup(backuphosts[serveridx]));
	}
    }

		/* 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 current 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{
	EM__(search_entries = NEWP(BuTapeEntry, num_backuphosts));
	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, T_("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 = earliest_bunum = 0;
    latest_timestamp = (time_t) 0;

    while(num_to_search > 0){		/* search through the storage units */
	stopidx = -1;
	if(num_search_entries) forever{			/* pseudo loop */
	  noservavail = NO;		/* 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 = YES;
	  }

	  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;

	  ZFREE(actstate.needed_tapes);

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

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

	  j = get_tapepos(&actcart, NULL, NULL, ACCESS_POSITION,
				search_entries[startidx].server,
				search_entries[startidx].port, NULL);
	  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 currently 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 */
	    EM__(tmpentries = NEWP(BuTapeEntry, num_to_search));

	    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, "J", identity,
			search_entries[idx].server,
			search_entries[idx].port, 0, cryptfile,
			search_entries[idx].cart, 1, 0, 0, 0, NULL);

	  pid = -1;				/* start scan program */
	  i = open_from_to_pipe(tmpbuf, pp, 1, &pid);
	  if(i){
	    fprintf(stderr, T_("Error: Cannot scan cartridge %d.\n"),
						(int) search_entries[idx].cart);
	    if(pid >= 0)
		waitpid_forced(pid, &pst, 0);
	  }
	  else{
	    UChar	frombuf[DEFCOMMBUFSIZ + 4], tobuf[DEFCOMMBUFSIZ + 4];
	    UChar	actclientid[256];
	    UChar	*tapebuf, *prefix, *postfix, *datestr, *tptr, c;
	    Flag	eot;
	    Int32	tapeblocksize, num_bytes_read, tapefilenum;
	    time_t	timestamp;

	    i = simple_cmd(SETSERIALOP, pp[0], pp[1]);
	    i = simple_cmd(SETERRORONEOT, pp[0], pp[1]);

	    tobuf[0] = QUERYTAPEBLOCKSIZE;
	    i = sendrcv(pp[0], frombuf, 5, pp[1], tobuf, 1);
	    xref_to_Uns32(&tapeblocksize, frombuf);

	    EM__(tapebuf = NEWP(UChar, tapeblocksize + DEFCOMMBUFSIZ));

	    eot = NO;
	    tapefilenum = 0;

	    while(!eot){
	      i = simple_cmd(OPENFORRAWREAD, pp[0], pp[1]);
	      if(i){
		eot = YES;
	      }
	      else{
		tapefilenum++;
		num_bytes_read = 0;
		memset(tapebuf, 0, tapeblocksize * sizeof(UChar));

		while(num_bytes_read < tapeblocksize){
		  tobuf[0] = READFROMTAPE;
		  i = sendrcv(pp[0], frombuf, DEFCOMMBUFSIZ + 1, pp[1], tobuf, 1);
		  if(i || (j = frombuf[DEFCOMMBUFSIZ])){
		    if(j && j != ENDOFFILEREACHED && j != ENDOFTAPEREACHED){
			errmsg(T_("Error: Cannot read enough bytes from tapefile"));
		    }

		    if(j == ENDOFTAPEREACHED)
			eot = YES;

		    break;
		  }

		  memcpy(tapebuf + num_bytes_read, frombuf,
						DEFCOMMBUFSIZ * sizeof(UChar));
		  num_bytes_read += DEFCOMMBUFSIZ;
		}

		tptr = tapebuf;
		while( (cptr = memfind(tptr, tapeblocksize - (tptr - tapebuf),
				EMREC_TAPE_PREFIX, l = strlen(EMREC_TAPE_PREFIX))) ){
		  tptr = (cptr += l);
		  prefix = strstr(cptr, EMREC_PREFIX);
		  postfix = strstr(cptr, EMREC_POSTFIX);
		  i = sscanf(cptr, "%d%d%n", &j, &k, &n);
		  if(prefix && postfix && postfix > prefix && i >= 2){
		    datestr = first_nospace(cptr + n);
		    c = *prefix;
		    *prefix = '\0';
		    timestamp = time_from_datestr(datestr);
		    *prefix = c;
		    postfix[strlen(EMREC_POSTFIX)] = '\0';

		    if(timestamp != (time_t) -1 && strlen(datestr) == j
				&& parity_byte(datestr, strlen(datestr)) == k){
		      cptr = prefix + strlen(EMREC_PREFIX);
		      if(sscanwordq(cptr, actclientid)){
			if( (j = !strcmp(actclientid, identity)) ){
			  if(timestamp > latest_timestamp){
			    *postfix = '\0';
			    strcpy(min_restore_info, prefix);
			    latest_timestamp = timestamp;
			  }

			  fprintf(stdout, "Info: ");
			}

			fprintf(stdout, T_("Found minimum restore info for client %s, "
					"backup date: %s"), actclientid,
					ctime(&timestamp));

			if(j){
			  fprintf(stdout, "%s\n", min_restore_info);
			  break;
			}
		      }	/* if we got a client identifier */
		    }	/* if there is a valid date specifier and the parity is ok */
		  }	/* if there is prefix, postfix and two numbers: len and par */
		}	/* while there are more tape prefixes in the tape buffer */

		i = simple_cmd(CLOSETAPEN, pp[0], pp[1]);

		tobuf[0] = SKIPFILES;
		Uns32_to_xref(tobuf + 1, 1);
		i = sendrcv(pp[0], frombuf, 1, pp[1], tobuf, 5);
		if(i){
		  eot = 1;
		}
	      }		/* if we could open the tape for rawread */
	    }		/* while no end of tape */

	    i = simple_cmd(GOODBYE, pp[0], pp[1]);
	    close(pp[1]);
	    i = waitpid_forced(pid, &pst, 0);
	    close(pp[0]);

	    if(tapefilenum < 2){
		errmsg(T_("Warning: There seems to be no data on tape %d"),
					(int) search_entries[idx].cart);
	    }
	    else{
		fprintf(stdout, T_("Scanned %d files on tape %d.\n"),
			(int) tapefilenum, (int) search_entries[idx].cart);
	    }
	  }		/* if we could start the client program */


	  if(!num_search_entries){	/* no cartridges ... given */
	    if(min_restore_info[0])
		break;
	  }
	}				/* 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 */

    if(!min_restore_info[0]){
	errmsg(T_("Error: No minimum restore info found, exiting"));
	do_exit(35);
    }

    fprintf(stdout, T_("\n%s minimum restore info of %s%s\n"),
			do_listing ? T_("Recommend") : T_("Using"),
			ctime(&latest_timestamp), min_restore_info);

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

    mininfoline = strdup(min_restore_info + strlen(EMREC_PREFIX));

    ZFREE(search_entries);
  }
  else{			/* read backup storing positions from stdin */
    fp = stdin;

    while(!feof(fp)){				/* read until eof */
      if(fget_realloc_str(fp, &line, &ncall))
	continue;

      if(! strncmp(line, EMREC_PREFIX, strlen(EMREC_PREFIX))){
	cptr = line + strlen(EMREC_PREFIX);

	ZFREE(clientname);
	clientname = strdup(cptr);
	sscanwordq(cptr, clientname);
	if(strcmp(clientname, identity))
	  continue;

	ZFREE(mininfoline);
	mininfoline = strdup(cptr);
      }
    }

    ZFREE(line);
    ncall = 0;
  }

  cptr = sscanwordq(mininfoline, NULL);
  servername = strword(cptr, 0);
  sscanf(word_start(cptr, 1), "%d%d%d", &i, &j, &k);
  serverport = i;
  cartno = j;
  fileno = k;
  filename = rfilename = strword(cptr, -1);

  if(restoreroot){
    while(FN_ISDIRSEP(*rfilename))
	rfilename++;
    if(!vardir_from_arg){
      while(FN_ISDIRSEP(*numfile))
	numfile++;
      while(FN_ISDIRSEP(*indexfilepart))
	indexfilepart++;
    }
  }

  if(empty_string(servername) || empty_string(filename)
		|| serverport <= 0 || cartno <= 0 || fileno <= 0){
    errmsg(T_("Format error in the following input line:\n%s\n"), mininfoline);
    do_exit(33);
  }

  free(mininfoline);

  if(verbose)
    fprintf(stdout, T_("Going to recover backup list file `%s' from "
		"server %s, port %d, cartridge %d, file %d\n"), rfilename,
		servername, (int) serverport, (int) cartno, (int) fileno);

  if(!access(rfilename, F_OK)){
	sprintf(tmpbuf, "%s.%d.", rfilename, getpid());
	time_t_to_intstr(time(NULL), tmpbuf + strlen(tmpbuf));
	if(rename(rfilename, tmpbuf))
	  errmsg(T_("Error: Could not rename file '%s`, will try to overwrite."
		" error: %s"), rfilename, strerror(errno));

	fprintf(stdout, T_("Info: Existing file `%s' is renamed to `%s'.\n"),
			rfilename, tmpbuf);
  }

  EM__(cptr = strapp("x ", filename));
  compose_clntcmd(tmpbuf, cptr, identity, servername, serverport, 0,
		cryptfile, cartno, fileno, 0, 0, 0, restoreroot);
  free(cptr);
  i = system(tmpbuf);
  if(i < 0 || WEXITSTATUS(i)){
    errmsg(T_("Error: Extracting the backup list returned bad status %d"),
				(int) i);
    do_exit(27);
  }
  if(access(rfilename, R_OK)){
    errmsg(T_("Error: Recovering backup list file `%s' failed"), rfilename);
    do_exit(32);
  }

  EM__(lines = read_asc_file(rfilename, &num_lines));

  cptr = FN_BASENAME(rfilename);
  if(strcmp(cptr, START_POSITIONS_FILE))
    unlink(rfilename);

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

  for(l = num_lines - 1, i = -1; l >= 0; l--){
    if(*(first_nospace(lines[l])) == '#')
	  continue;

    if(sscanf(lines[l], "%d", &i) > 0)
	break;
  }
  if(i < 0){
    errmsg(T_("Error: No backup specifier lines found in `%s'"), rfilename);
    do_exit(23);
  }
  act_bunum = i;

  for(l = num_lines - 1; l >= 0; l--){
    if(*(first_nospace(lines[l])) == '#'){
	free(lines[l]);
	memmove(lines + l, lines + l + 1, (num_lines - l) * sizeof(UChar *));
	num_lines--;
	continue;
    }
  }

  havelastfull = NO;
  latest_bunum = -1;
  for(l = num_lines - 1; l >= 0; l--){
    i = sscanf(lines[l], "%d.%d/%d", &bunum, &isp, &nump);
    if(i > 0){
	if(!havelastfull){
	  if(i >= 3){
	    havelastfull = YES;
	    latest_bupart = isp;
	    num_latest_buparts = nump;
	  }
	}
	if(latest_bunum < 0){
	  latest_bunum = bunum;
	}

	if(havelastfull && latest_bupart == num_latest_buparts
						&& bunum < latest_bunum)
	  break;		/* we have a complete full_backup here */
	if(havelastfull && isp <= latest_bupart && bunum < latest_bunum
						&& ! one_more_for_restore)
	  break;		/* have all parts for a complete backup */
	if(bunum < latest_bunum - 1)
	  break;			/* not it's enough in any case */
    }
  }
  if(l < num_lines - 1)		/* the above loop stopped befor what we need */
    l++;

		/* fill the cartridges and files list from the read lines */
  for(num_backups = 0; l < num_lines; l++){
    success = (sscanf(lines[l], "%d", &bunum) >= 1);
    servers[num_backups] = strword(lines[l], 1);
    cptr = word_start(lines[l], 2);
    success &= (sscanf(cptr, "%d%d%d", &i, &j, &k) >= 3);

    if(!success){
	free(servers[num_backups]);
    }
    else{
	ports[num_backups] = i;
	carts[num_backups] = j;
	files[num_backups] = k;
	bunums[num_backups] = bunum;
	num_backups++;
    }
  }

  sprintf(tmpbuf, "%d", act_bunum);
  indexfile = strapp(indexfilepart, tmpbuf);
  zindexfile = strapp(indexfile, COMPRESS_SUFFIX);

  sprintf(tmpbuf, T_("Filename logfile for backup number %d"),
						(int) act_bunum);
  if(rename_if_exists(indexfile, tmpbuf)
			|| rename_if_exists(zindexfile, tmpbuf))
    do_exit(36);

  if(mkbasedir(indexfile, 0755, getuid(), getgid()) < 0){
    errmsg(T_("Error: cannot create directory for `%s'"), indexfile);
    do_exit(46);
  }
  if(mkbasedir(numfile, 0755, getuid(), getgid()) < 0){
    errmsg(T_("Error: cannot create directory for `%s'"), numfile);
    do_exit(47);
  }

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

  for(i = 0; i < num_backups; i++){
    if(!restore_emlist){
      fprintf(stdout,
		T_("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, preserve_existing ? "xvnlaOU" : "xvnlaOUu",
		identity, servers[i], ports[i], 0,
		cryptfile, carts[i], files[i], time_older, time_newer,
				0, restoreroot);
    }
    else{
      fprintf(stdout,
		T_("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, "tnlOU", identity, 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, T_("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(T_("Warning: Writing to filename logfile failed"));
	  if(j == 9)
	    errmsg(T_("Further warnings suppressed"));
	}
      }
      write(1, tmpbuf, l);
    }
    close(fd);
    waitpid_forced(pid, &pst, 0);

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

    write(ofd, "\n", 1);
  }

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

    unzipfile = hidden_filename(zindexfile);
    sprintf(tmpbuf,
	T_("File to contain index unprocess command for backup number %d"),
						(int) act_bunum);
    if(rename_if_exists(unzipfile, tmpbuf))
	do_exit(37);

    fp = fopen(unzipfile, "w");
    if(fp){
	fprintf(fp, "%s\n",
		(idxuncompresscmd[0] ? idxuncompresscmd : (UChar *) "gunzip"));
	fclose(fp);
    }
    else{
	fprintf(stderr, "Error: Cannot open file `%s'.\n", unzipfile);
    }
  }

  if(!read_pint_file(numfile, &bunum)){	/* there is a number file !!! */
    k = MIN(bunum, act_bunum);		/* create missing indexes between */
    l = MAX(bunum, act_bunum);		/* currently created and the one */
    for(i = k; i <= l; i++){		/* found in the num file */
	sprintf(tmpbuf, "%d", i);
	indexfile = strapp(indexfilepart, tmpbuf);
	zindexfile = strapp(indexfile, COMPRESS_SUFFIX);
	unzipfile = hidden_filename(zindexfile);

	if(access(indexfile, F_OK) && access(zindexfile, F_OK)){
	  fd = open(indexfile, O_CREAT | O_WRONLY | O_TRUNC, 0600);
	  if(fd >= 0){			/* just "touch" the file */
	    close(fd);
	  }
	  else{
	    fprintf(stderr, T_("Error: Cannot create file `%s'.\n"), indexfile);
	  }
	  unlink(unzipfile);
	}

	free(indexfile);
	free(zindexfile);
	free(unzipfile);
    }
  }
	/* the current restored number is higher than in the file or there */
  if(bunum < act_bunum){	/* is no file (read_pint_file sets to 0) */
    if(write_uns_file(numfile, act_bunum)){
      fprintf(stderr, T_("Warning: Cannot write file \"%s\".\n"), numfile);
    }
  }

  do_exit(0);
}

static void
copy_tape(int argc, char ** argv)
{
  Int32		i, filenum = 0;
  UChar		*sourcehost = NULL, *targethost = NULL;
  UChar		*sourcekeyfile = NULL, *targetkeyfile = NULL;
  UChar		*targetportstr = NULL, *sourceportstr = NULL;
  Int32		sourceport = 0, targetport = 0;
  Int32		sourcecart = 0, targetcart = 0;
  char		**cpptr, *cv[25];

  for(i = 1; i < argc; i++)
    if(!strncmp(argv[i], "-D", 2))
	break;

  if(i < argc){
    goptions(argc - i, (UChar **) argv + i,
		"s:h;s:k;s:P;i:C;i:F;s:c;s:l;b:v;s:T",
		&targethost, &targetkeyfile, &targetportstr, &targetcart,
		&filenum, &paramfile, &logfile, &verbose, &tmpdir);
    argc = i;
    EM__(cpptr = NEWP(char *, i + 1));

    memcpy(cpptr, argv, argc * sizeof(char *));
    argv = cpptr;

    argv[i] = NULL;

    if(targetportstr){
	targetport = get_tcp_portnum(targetportstr);
	if(targetport < 0){
	  errmsg(T_("Error: Cannot resolve TCP service name `%s'.\n"),
			targetportstr);
	  do_exit(1);
	}
    }
  }

  goptions(argc, (UChar **) argv,
		"s:h;s:k;s:P;i:C;i:F;s:c;s:l;b:v;s:T",
		&sourcehost, &sourcekeyfile, &sourceportstr, &sourcecart,
		&filenum, &paramfile, &logfile, &verbose, &tmpdir);

  if(!sourcehost)
    sourcehost = backuphosts[0];
  if(!sourceportstr)
    sourceport = backupports[0];
  if(!sourcekeyfile)
    sourcekeyfile = cryptfile;

  if(sourceportstr){
    sourceport = get_tcp_portnum(sourceportstr);
    if(sourceport < 0){
	errmsg(T_("Error: Cannot resolve TCP service name `%s'.\n"),
			sourceportstr);
	do_exit(1);
    }
  }

  i = 0;
  cv[i++] = clientprogram;
  if(verbose){
    cv[i++] = "-v";
  }
  if(sourcecart){
    cv[i++] = "-C";
    sprintf(tmpbuf, "%d", (int) sourcecart);
    cv[i++] = strdup(tmpbuf);
  }
  if(filenum){
    cv[i++] = "-F";
    sprintf(tmpbuf, "%d", (int) filenum);
    cv[i++] = strdup(tmpbuf);
  }
  if(sourcehost){
    cv[i++] = "-h";
    cv[i++] = sourcehost;
  }
  if(sourceport > 0){
    cv[i++] = "-p";
    sprintf(tmpbuf, "%d", (int) sourceport);
    cv[i++] = strdup(tmpbuf);
  }
  if(sourcekeyfile){
    cv[i++] = "-k";
    cv[i++] = sourcekeyfile;
  }
  if(targetcart || targethost || targetport || targetkeyfile){
    strcpy(tmpbuf, "-D ");
    if(targetcart){
	sprintf(tmpbuf + strlen(tmpbuf), "%d", (int) targetcart);
    }
    if(targethost){
	strcat(tmpbuf, SEPLOC);
	strcat(tmpbuf, targethost);
    }
    if(targetport){
	strcat(tmpbuf, PORTSEP);
	sprintf(tmpbuf + strlen(tmpbuf), "%d", (int) targetport);
    }
    if(targetkeyfile){
	strcat(tmpbuf, ":");
	strcat(tmpbuf, targetkeyfile);
    }

    cv[i++] = strdup(tmpbuf);
  }
  if(tmpdir){
    cv[i++] = "-T";
    cv[i++] = tmpdir;
  }

  cv[i] = NULL;

  execv(clientprogram, cv);

  errmsg(T_("Error: Cannot execute %s"), clientprogram);
  do_exit(31);
}
