/* Tape structure
 * 
 * Taper header (at beginning of each tape)
 * ----------------------------------------
 * long taper_magic number  (identifies taper backups  )
 * long archive ID (unqiue # that IDs archives         ) 
 * int tape number                                     )
 * char archive_title 
 * 
 * 
 * Volume
 * ------
 * long volume_magic
 * long no_in_vol
 * 
 * DATA
 *               : file_info
 *               : file_name
 *               : file (straight or compressed) only if file IS_IFREG
 *               :    S_ISDIR   nothing
 *               :    S_ISLNK   the name of the symbolic link (# chars first)
 * 
 * 
 * File info block:
 * 
 *   long   actual size  with uncompressed files, = size
 *   volume           
 *   position in vol  
 *   rdev             )
 *   uid              )
 *   gid              )
 *   mode             )     Taken from stat call
 *   org mode (mode of original file for links)
 *   size             )
 *   a time           ) 
 *   m time           )
 *   c time           )  
 *   backup time 
 *   length of name+1
 *   compressed or not compressed
 *   checksum
 *
 * 
 * Information file structure
 * 
 * long archive_id                        )
 * int number_tapes                       )   in structure
 * long info_file_size                    )
 * int number_volumes                     )
 * long no_in_archive                     ) 
 * char archive_title                     )  
 * 
 * For each file:
 *       file_info
 *       file_name
 * 
 * At the end of the file
 *       volume    tape it's on   - for each volume
 * 
 * 
 * Args
 *        +/-a    = append files to archive (default on)
 *        -b ttt  = name of both rewinding & non-rewinding tape device
 *        +/-c    = compression on or off (default = on)
 *        -f ttt  = name of rewinding tape device
 *        +/-h    = hard links on or off  (default = off)
 *        -i x    = where the archive information files are
 *        -j num  = device on which /proc is mounted (default 1; 0 disable)
 *        -l fff  = name of log file
 *        -m num  = level of logging (0=none...3=verbose)
 *        -n ttt  = name of non-rewinding tape device
 *        -p xx   = name of preferences file (default = .taper in $HOME/.taper)
 *        -s num  = number of leading paths to strip (default 0)
 *        -t xx   = name to give volume
 *        -v      = prints version
 *        -x num  = maximum # of bytes to transfer to/from device in one hit
 *        +/-y    = turns on/off alpha sort of directory list (default on)
 *        +/-z    = turns on/off confirmation of directory selection (default on)
 *        -?      = prints command line help
 * 
 * Pathname stripping
 *   This doesn't affect the backup. It just affects how files in the
 *   right hand top window look (ie. what's on the archive already). See
 *   the section in restore for explanation of pathname stripping
 * 
 * Directory confirmation
 *   If on, then every time the user selects a directory, s/he is asked for
 *   confirmtion before the actions is processed. If off, then there is
 *   no confirmation. NOTE: that confirmation is always sought before a 
 *   delete.
 * 
 * Preferences
 *   The name of a preferences file. Preferences will be read from
 *   this file if it exists. Note command line options override preference
 *   file settings.
 * 
 * Append
 *   If a valid archive is found on backup device, and if this option is
 *   true, then the files are added to this archive.
 *   If off, then the existing archive is erased and
 *     files selected over-write what was on backup device.
 * 
 * Hard links.
 *   When a link is encounterd, normally, just the link is copied.
 *   If this option is on, the actual file is copied.
 * 
 * Compression
 *   If on, then all files are compressed before being archived.
 *   They are compressed using an external compression program, 
 *   as specified in the header files (usually gzip)
 * 
 * Archive Information Files
 *   For each archive, there is a file on the hard disk that contains
 *   information about the archive. The default place for these archive
 *   information files is in the /$HOME/.taper directory (created if doesn't
 *   exist).
 * 
 * Log File
 *   Name of the file to log information to. Default is .taper_log and 
 *   logging info is appended to this file
 * 
 * Log Level
 *   Level of logging - 0=none..3=verbose
 * 
 * /proc device
 *   Most UN*Xes have the option for having a /proc directory in which
 *   details of currently running processes and system information (eg.
 *   current RAM) is stored. We obviously don't want to backup this
 *   runtime information. You can specify the number of the /proc device
 *   (usually 1) and taper will ignore all files on this device. If you
 *   give a value of 0, then this option is disabled. The default value
 *   is one.
 * 
 * Note: If a file can't be written to the tape device for some reason
 * or another, then the checksum of the file is set to -1. This is because
 * we still need to write out the filename etc.. to keep the file & 
 * volume counts correct.
 */

#include "common.h"
#include "backup.h"

/* Window variables required */
WINDOW    *on_vol;				 /* status is not in restore, so not common */


/* Pointers, counts etc.. */
struct direntry      *directory;		 /* points to the directory */



void usage()
{
    fprintf(stderr, "\n\n");
    fprintf(stderr, "Backup command line options\n");
    fprintf(stderr, "----------------------------\n");
    common_usage();
    fprintf(stderr, "+/- a   (--append-[on|off])       turns on/off append\n");
    fprintf(stderr, "+/- c   (--compress-[on|off])     turns on/off compression\n");
    fprintf(stderr, "+/- h   (--hard-links-[on|off])   turns on/off storing of links or actual files\n");
    fprintf(stderr, "-j num  (--proc-device num)       device number of /proc system\n");
    fprintf(stderr, "-t name (--archive-title)         title of archive\n");
    fprintf(stderr, "-u      (--update)                update archive\n");
    fprintf(stderr, "\n\n");
    do_exit(ERROR_USAGE);
}


void parse_args(int argc, char *argv[])
{
    int c=0;
    char z;
    
    while (++c<argc) {
	z = common_parse_args(argc,argv, &c);	 /* look for common args */
	if (z==-1) usage();
	if (z==0) {
	    if (argv[c][1] == '-') {		 /* GNU style long option */
		if (!strcasecmp(argv[c], "--append")) {
		    if ((append = on_or_off(argv[c])) == -1)
		      usage();
		    break;
		}
		if (!strcasecmp(argv[c], "--compress")) {
		    if ((compression = on_or_off(argv[c])) == -1)
		      usage();
		    break;
		}
		if (!strcasecmp(argv[c], "--hard-links")) {
		    if ((hard_links = on_or_off(argv[c])) == -1)
		      usage();
		    break;
		}
		if (!strcasecmp(argv[c], "--archive-title"))
		  argv[c][1] = 't';
		if (!strcasecmp(argv[c], "--update"))
		  argv[c][1] = 'u';
		if (!strcasecmp(argv[c], "--proc-device"))
		  argv[c][1] = 'j';
	    }
	    switch (argv[c][1]) {			 /* no common args */
	     case 'u':
	     case 'U':
		update = 1;
		break;
	     case 't':
	     case 'T':
		c++;
		if (c==argc)			 /* make sure that they've supplied */
		  usage();			 /* another arg */
		if (strlen(argv[c]) < MAX_ARCHIVETITLE) 
		  strcpy(archive_title, argv[c]);
		else 
		  memcpy(archive_title, argv[c], MAX_ARCHIVETITLE);
		archive_title[MAX_ARCHIVETITLE] = 0;
		break;
	     case 'j':				 /* proc device */
	     case 'J':
		c++;
		if (c==argc)			 /* make sure that they've supplied */
		  usage();			 /* another arg */
		proc_dev = atol(argv[c]);
		break;
	     case 'a':				 /* append to volume */
	     case 'A':
		append = (argv[c][0] == '-') ? 0 : 1;
		break;
	     case 'C':				 /* use compression */
	     case 'c':
		compression = (argv[c][0] == '-') ? 0 : 1;
		break;
	     case 'H':				 /* hard links */
	     case 'h':
		hard_links = (argv[c][0] == '-') ? 0 : 1;
		break;
	     default:
		usage();
	    }
	}
    }
}
    

void add_one_file(WINDOW *mes_box, char **begbuf, struct file_info *fi, char *name)
{
    long pos;
    
    ifd.info_file_size += sizeof(struct file_info) + fi->name_len;
    pos = cp - *begbuf;				 /* keep cp position */
    *begbuf = my_realloc(*begbuf, ifd.info_file_size);
    cp = *begbuf + pos;
    ifd.no_in_archive++;
    memcpy(cp, fi, sizeof(struct file_info));
    cp += sizeof(struct file_info);
    strcpy(cp, name); cp += strlen(name)+1;
}


void add_to_dir(WINDOW *mes_box, char **begbuf, char *file) 
{
    /* adds file to volume directory */
    struct stat b, borg;
    int e;
    struct dirent *x;
    DIR  *dir_ptr;
    char old_dir[MAXNAMLEN];
    struct file_info fi;
    
    if (hard_links) {
	e=stat(file, &b);
	if ((e==-1) && (errno==ENOENT))		 /* no link exists */
	  e=lstat(file, &b);
    }
    else
      e=lstat(file, &b);
    stat(file, &borg);
    if (e==-1) 
      do_exit(ERROR_GETINFO);

    if (proc_dev) 				 /* if /proc device checking is enabled */
      if ( ((hard_links) &&(borg.st_dev == proc_dev)) ||   /* check if we are going to be backing */
	  (!hard_links && (b.st_dev == proc_dev)) )   /* up a /proc device */
	  return;				 /* ignore it */

    fi.checksum = 0;
    fi.act_size = 0;
    fi.dev = b.st_rdev;
    fi.uid = b.st_uid;
    fi.gid = b.st_gid;
    fi.mode = b.st_mode;
    fi.org_mode = borg.st_mode;
    fi.size = b.st_size;
    fi.atime = b.st_atime;
    fi.mtime = b.st_mtime;
    fi.ctime = b.st_ctime;
    fi.backup_time = time(NULL);		 /* backup time */
    fi.name_len = strlen(file)+1;
    fi.volume = ifd.number_volumes;		 /* show which volume it belongs to */
    fi.pos_in_archive = 0;				 /* initialise */
    add_one_file(mes_box, begbuf, &fi, file);	 /* put file/info into volume directory */
    if (!S_ISDIR(b.st_mode))			 /* not a directory */
      return;					 /* file info is copied */

    if ((dir_ptr = opendir(file)) == NULL)	 /* open directory */
      if (errno == EACCES)			 /* unable to access */
      return;					 
    else
      do_exit(ERROR_ILLEGAL_DIR);		 /* problem opening dir */

    x=(struct dirent *) 1;
    while (x) {					 /* read into memory */
	x = readdir(dir_ptr);
	if (x) {
	    if (!strcmp(x->d_name, "."))
	      continue;
	    if (!strcmp(x->d_name, ".."))
	      continue;
	    strcpy(old_dir, file);
	    if (old_dir[strlen(old_dir)-1] != '/')
	      strcat(old_dir, "/");
	    strcat(old_dir, x->d_name);
	    if (!strcmp(old_dir, tape))		 /* don't backup backup device */
	      continue;
	    add_to_dir(mes_box, begbuf, old_dir);/* add file to list */
	}
    }
    closedir(dir_ptr);
}

inline void add_actual_files(WINDOW *mes_box, char **info)
{
    long count;
    
    for (count=0; count<no_sel; count++)
      add_to_dir(mes_box, info, (sel_files+count)->name);
}


void write_nameinfo(struct file_info *fi, char *cp)
{
/* An error writing to the backup device is a fatal error */
    char l[MAXNAMLEN+50];
    
    sprintf(l, "Writing info & filename for %s", cp);
    if (tape_write((char *) fi, sizeof(struct file_info)) == -1) {
	write_fatal_log(l);			 /* write info */
	do_exit(ERROR_WRITING);
    }
    if (tape_write((char *) cp, strlen(cp)+1) == -1) {/*  write filename */
	write_fatal_log(l);
	do_exit(ERROR_WRITING);
    }
}


void backup_file(long pos_in_archive)
{
/* Backup the file pointed to by cp and update cp 
 * An error writing to the backup device is a fatal error */
    
    struct file_info *fi;
    char   tmpf[MAXNAMLEN];
    char   s[MAXNAMLEN*2], *fn, l[MAXNAMLEN+50];
    int    fd;
    long   x, orgfsz, totalread;
    struct stat sbuf;
    
    fi = (struct file_info *) cp;		 /* fi points to file info */
    fn = cp + sizeof(struct file_info);
    fi->pos_in_archive = pos_in_archive;	 /* set pos in volume */
    
    if (S_ISLNK(fi->mode)) {			 /* a soft link */
	memset(tmpf, 0, sizeof(tmpf));		 /* name of original file */
	readlink(fn, tmpf, sizeof(tmpf));	 /* is written */
	fi->checksum = 0;
	write_nameinfo( fi, fn);
	x = strlen(tmpf)+1;
	sprintf(l, "Writing link name for %s [%s]", fn, tmpf);
	if (log_level > 1) write_log(l);
	if (tape_write((char *) &x, sizeof(x)) == -1) {
	    write_fatal_log(l);
	    do_exit(ERROR_WRITING);
	}
	if (tape_write((char *) tmpf, x) == -1) {
	    write_fatal_log(l);
	    do_exit(ERROR_WRITING);
	}
	return;
    }
	
    if (!(S_ISREG(fi->mode))) { 		 /* if file isn't an ordinary */
	fi->checksum = 0;
	sprintf(l, "Storing directory %s", fn);
	if (log_level > 1) write_log(l);
	write_nameinfo( fi, fn);		 /* file, only write out name */
	return;					 /* and info */
    }

    *tmpf = 0;					 /* must be a regular file */

    fi->compressed = 0;				 /* not compressed */
    if (compression) {				 /* compress if need be */
	sprintf(l, "Opening %s", fn);
	if (log_level == 3) write_log(l);
	fd = open(fn, O_RDONLY);		 /* ensure I can read the file */
	if (fd == -1) {				 /* I can't */
	    write_error_log(l);
	    fi->checksum = -1;			 /* mark file as invalid */
	    write_nameinfo( fi, fn);
	    return;
	}
	sprintf(l, "Getting file status info for %s", fn);
	if (log_level == 3) write_log(l);
	if (fstat(fd, &sbuf) == -1) {			 /* get actual file size */
	    write_error_log(l);
	    fi->checksum = -1;			 /* mark file as invalid */
	    write_nameinfo( fi, fn);
	    return;
	}
	orgfsz = sbuf.st_size;
	close(fd);
	tmpnam(tmpf);				 /* compress to temp file */
	sprintf(s, "%s \"%s\" > %s", COMPRESS_PROG, fn, tmpf);   /* compress file */
	change_dollar(s);
	sprintf(l, "Compressing %s to %s", fn, tmpf);
	if (log_level == 3) write_log(l);
	if (system(s)) {			  
	    write_error_log(l);
	    fi->checksum = -1;			 /* mark file as invalid */
	    write_nameinfo( fi, fn);
	    return;
	}
	sprintf(l, "Opening compressed file %s", tmpf);
	if (log_level == 3) write_log(l);
	fd = open(tmpf, O_RDONLY);		 /* open the temp file */
	if (fd == -1) {
	    write_error_log(l);
	    unlink(tmpf);			 /* compressed file, delete it */
	    fi->checksum = -1;			 /* mark file as invalid */
	    write_nameinfo( fi, fn);
	    return;
	}
	sprintf(l, "Getting compressed file status info for %s", tmpf);
	if (log_level == 3) write_log(l);
	if (fstat(fd, &sbuf) == -1) {		 /* get compressed file size */
	    write_error_log(l);
	    fi->checksum = -1;			 /* mark file as invalid */
	    write_nameinfo( fi, fn);
	    unlink(tmpf);			 /* compressed file, delete it */
	    return;
	}
	
	fi->compressed = 1;			 /* file is compressed */
	if (sbuf.st_size >= orgfsz) {		 /* if compressed size >= org size */
	    close(fd);				 /* use original file */
	    sprintf(l, "Reopening original file %s (compressed file was larger)", fn);
	    if (log_level == 3) write_log(l);
	    fd = open(fn, O_RDONLY);
	    if (fd == -1) {
		write_error_log(l);
		fi->checksum = -1;		 /* mark file as invalid */
		write_nameinfo( fi, fn);
		unlink(tmpf);			 /* compressed file, delete it */
		return;
	    }
	    
	    fi->compressed = 0;			 /* file is not compressed */
	}
    }
    else {
	sprintf(l, "Opening file %s", fn);
	if (log_level == 3) write_log(l);
	fd = open(fn, O_RDONLY);		 /* open source file */
	if (fd == -1) {
	    write_error_log(l);
	    fi->checksum = -1;			 /* mark file as invalid */
	    write_nameinfo( fi, fn);
	    return;
	}
    }
    
    sprintf(l, "Getting file status info for %s", fn);
    if (log_level == 3) write_log(l);
    if (fstat(fd, &sbuf) == -1) {		 /* get actual file size */
	write_error_log(l);
	fi->checksum = -1;			 /* mark file as invalid */
	write_nameinfo( fi, fn);
	return;
    }

    fi->act_size = sbuf.st_size;		 /* update file info */
    fi->checksum = calc_checksum(fd);		 /* calculate checksum */

    write_nameinfo(fi, fn);			 /* write info/name */
    if (fi->checksum == -1)			 /* couldn't get checksum */
      return;
    totalread = 0;
    if (log_level > 1) {
	sprintf(l, "Backing up file %s; actual size %ld, on tape size %ld.", 
		fn, fi->size, fi->act_size);
	write_log(l);
    }
    while (1) {					 /* copy file accross */
	x = read(fd, tr_buffer, max_tr_size);	 /* to device file   */
	if (!x) break;
	if (x == -1) {
	    write_error_log("Error reading file while transferring data to backup device");
	    while (totalread < fi->act_size) 
	      totalread += tape_write((char *) tr_buffer, min(max_tr_size,/* pad rubbish data to the end so */
				       fi->act_size - totalread));
	    break;				 /* so that the archive integrity is maintained */
	}
	totalread += x;				
	x = tape_write((char *) tr_buffer, x);   
	if (x == -1) 
	  do_exit(ERROR_WRITING);
    }
    close(fd); 
    
    if (*tmpf)					 /* remove temporary files */
      unlink(tmpf);
}


void  do_backup(char *cur_dir) {				 
    long   count=0, no_old_files, bytes_processed, bytes_written;
    WINDOW *mes=NULL;
    char   s[100], s2[20], s3[20];
    time_t t_start, t_current;
    struct volume_tape_info vti;
    int    fd, v, c;
    
    if (!no_sel)				 /* none selected */
      return;
    mes = status_box(mes, "          Opening backup device...          ", 2, TRUE, 5);
    if (info == NULL) {				 /* no info file - must be creating a new archive */
	info = my_malloc(sizeof(struct info_file_header));
	append = 0;				 /* nothing in volume - can't append */
    }
    if (!info)					 /* can't allocate memory */
      do_exit(ERROR_MEMORY);

    dv = tape_create(O_RDWR|O_CREAT, S_IREAD|S_IWRITE);/* open non-rewinding */
    if (dv == -1) 
	do_exit(ERROR_OPENING_BACKUP);
    status_box(mes, "Adding to volume directory...", 2, FALSE, 1);
    if (!append) {
	tdh.magic = TAPER_MAGIC_NUMBER;		 /* identify our directory volume */
	tdh.tape_number = 1;			 /* first volume */
	ifd.archive_id = tdh.archive_id;	 /* setup info file header */
	ifd.info_file_size = sizeof(struct info_file_header);
	ifd.number_tapes = 1;
	ifd.number_volumes = 1;
	ifd.no_in_archive = 0;
	strcpy(ifd.archive_title, archive_title);
	strcpy(tdh.archive_title, archive_title);
	no_old_files = 0;
	vt_info = my_realloc(vt_info, sizeof(struct volume_tape_info));
    }
    else {
	ifd.number_volumes++;
	no_old_files = ifd.no_in_archive;
	vt_info = my_realloc(vt_info, sizeof(struct volume_tape_info)*ifd.number_volumes);
    }

    vti.volume = ifd.number_volumes;		 /* update volume/tape info */
    vti.tape = ifd.number_tapes;
    memcpy(vt_info+ifd.number_volumes-1, &vti, sizeof(vti));   
    cp = info + sizeof(struct info_file_header);
    if (append) 				 /* move past existing entries */
	for (count=0; count < no_old_files; count++) {
	  cp += sizeof(struct file_info);
	  cp += strlen(cp)+1;
	}

    add_actual_files(mes, &info);		 /* add filenames to directory */

    if (append) {
	if (check_tape(ifd.number_tapes) == -1)	 /* make sure correct tape in drive */
	  return;				 /* user aborted or error */
 	status_box(mes, "Advancing past existing entries...", 2, FALSE, 1);
	calc_tape_pos(ifd.number_volumes, &c, &v);/* work out how many volumes to advance */
	tape_close(dv);				 /* close rewinding device opened earlier */
	dv = ntape_open(O_RDWR);		 /* now open non-rewinding so we can position */
	tape_fsf(v);				 /* reposition */
	if (!is_regfile(dv)) {			 /* if using a tape, then need to reposition like this */
	    tape_close(dv);			 /* close it to effect change */
	    dv = tape_open(O_RDWR);		 /* reopen at new position (rewinding device)*/
	}
    }
    else {
	if (log_level == 3) write_log("Writing tape header");
	  write(dv, &tdh, sizeof(struct tape_header));
    }
    
    count = VOLUME_MAGIC;			 /* write volume magic number */
    if (log_level == 3) write_log("Writing volume magic number");
    tape_write((char *) &count, sizeof(count));
    count = ifd.no_in_archive - no_old_files;	 /* write out how many files in this volume */
    if (log_level == 3) write_log("Writing number of files on volume");
    tape_write((char *) &count, sizeof(count));
    bytes_written=0;
    bytes_processed=0;
    status_box(mes, "", 2, FALSE, 1);
    convert(s3, total_selected);
    t_start = time(NULL);
    cp = info + sizeof(struct info_file_header);
    for (count=0; count< ifd.no_in_archive; count++) {
	if (((struct file_info *) cp)->volume == ifd.number_volumes) {  /* only need new ones */
	    status_box(mes, cp+sizeof(struct file_info), 0, FALSE, 1);
	    sprintf(s, "File %ld of %ld", count, ifd.no_in_archive);
	    status_box(mes, s, 2, FALSE, 1);
	    sprintf(s, "Processed byte %s of %s", convert(s2, bytes_processed), s3);
	    status_box(mes, s, 3, FALSE, 1);
	    if (bytes_written)
	      sprintf(s, "Written %s bytes: Ratio %.2f",
		      convert(s2, bytes_written), 
		      (float) bytes_processed/(float) bytes_written);
	    else
	      sprintf(s, "Written %s bytes: Ratio 1.0",
		      convert(s2, bytes_written));
	    status_box(mes, s, 4, FALSE, 1);
	    t_current = time(NULL);
	    if (total_selected)
	      sprintf(s, "%.0f%% done, time elapsed %s",  (float) bytes_processed/(float) total_selected * 100,
		      convtime(t_start, t_current));
	    else				 /* if bytes=0, base on # files */
	      sprintf(s, "%ld%% done, time elapsed %s",  100L*count/ifd.no_in_archive,
		      convtime(t_start, t_current));
	    status_box(mes, s, 6, FALSE, 1);
	    sprintf(s, "Average backup rate %ld K/sec",
		    ((t_current-t_start) ? (bytes_written/1024)/(t_current-t_start) : 0));
	    status_box(mes, s, 7, FALSE, 1);
	    backup_file(++no_old_files);
	    bytes_processed += ((struct file_info *) cp)->size + sizeof(struct file_info) + 1 + strlen(cp+sizeof(struct file_info));
	    bytes_written += ((struct file_info *) cp)->act_size + sizeof(struct file_info) + 1 + strlen(cp+sizeof(struct file_info));
	}
	cp += sizeof(struct file_info);
	cp += strlen(cp)+1;
    }
    status_box(mes, "", 0, FALSE, 1);
    status_box(mes, "", 2, FALSE, 1);
    status_box(mes, "", 4, FALSE, 1);
    status_box(mes, "", 6, FALSE, 1);
    status_box(mes, "Closing tape", 3, FALSE, 1);
    tape_close(dv);				 /* rewinding device so will auto update headers */
    memcpy(info, &ifd, sizeof(struct info_file_header));
    fd = open_info_file(FALSE, ifd.archive_id);
    if (log_level == 3) write_log("Writing info file information");
    write(fd, info, ifd.info_file_size);
    write(fd, vt_info, ifd.number_volumes*sizeof(struct volume_tape_info));
    if (log_level == 3) write_log("Closing info file");
    close(fd);
    close_statusbox(mes);
}

   
void init_windows() {				 
/*  Initialise windows and print screen edges etc..      */

    int       right_startx;
    
    win_main = initscr();			 /* Initialize the cursor library */    
    start_color(); setup_colors();		 /* setup colors */

    raw(); noecho(); cbreak();
    
    screen_xlen=win_main->_maxx+1;		 /* Dimensions of screen */
    screen_ylen=win_main->_maxy;
    
    screen_ylen_files = screen_ylen/2 +4;
    screen_ylen_selection = screen_ylen - screen_ylen_files -1;
    left_width = screen_xlen*2/3;		 /* left screen is 2/3 of screen */
    right_width = screen_xlen-left_width;	 
    right_startx = left_width;
    
    title = newwin(1, screen_xlen, 0, 0);	 /* title bar */
    files = newwin(screen_ylen_files, left_width,
		  1, 0);			 /* files hand window */
    on_vol = newwin(screen_ylen_files, right_width,
		  1, right_startx);		 /* files hand window */
    selection = newwin(screen_ylen_selection, screen_xlen,/* right hand window */
		   screen_ylen_files+1, 0);
    bottom = newwin(1, screen_xlen, screen_ylen, 0);
    keypad(title, TRUE);
    keypad(on_vol, TRUE); 			 
    keypad(selection, TRUE); 			 
    keypad(files, TRUE);			 /* set input modes */
}


void clear_screen()
{
    my_werase(on_vol, COLOR_ONVOL); my_werase(files, COLOR_DIRECTORY); 
    my_werase(selection, COLOR_SELECTED); my_werase(bottom, COLOR_BOTTOM);
    box(on_vol, ACS_VLINE, ACS_HLINE);
    box(files, ACS_VLINE, ACS_HLINE);		 /* draw boxes around */
    box(selection, ACS_VLINE, ACS_HLINE);	 /* screens */
    wrefresh(on_vol); wrefresh(files); wrefresh(selection);
    
    curs_set(0);
}

int check_backup() {
/* Make sure backup is a valid device and confirm with use about
   an erase if it has been requested.         */
    int x;
    WINDOW *mes=NULL;

    x = do_read_vol_dir(tape, TRUE, O_RDWR, append, FALSE);
    if (x == BAD_MAGIC)
      if (bmessage_box("Unknown tape data. Overwrite?", MB_YESNO)) append = 0;
      else return 0;;

    if ((x == TAPE_EXIST) && (!append)) 
	if (!bmessage_box("Taper data on tape. Confirm erase", MB_YESNO))
	  return 0;
    
    if (x == TAPE_EXIST_EMPTY) {		 /* empty taper tape */
	append = 0;
	return 1;
    }
    
    if ((x == BAD_MAGIC) || (!append)) {	 /* no taper data on tape */
	append = 0;
	mes = status_box(mes, "Erasing data on tape", 1, TRUE, 1);
	dv = tape_open(O_RDWR);
	tape_erase(dv);
	tape_close();
	close_statusbox(mes);
	return 1;
    }
    
    if (x == TAPE_NOEXIST) {
	append = 0;
	return 1;
    }
    
    return 1;
}


void find_update_files()
{
/* Looks through archive and checks which files need updating 
   Ignores directories that have been updated */
    long    c;
    struct  stat statbuf;
    WINDOW  *mes=NULL;
    struct  file_entry *fe;
    char    s[MAXNAMLEN];

    if (!append) return;			 /* nothing in archive */
    mes = status_box(mes, "Working out which files need updating", 2, TRUE, 2);
    fe = vol_files;
    for (c=0; c<ifd.no_in_archive; c++) {
	strcpy(s, cf); strcat(s, "/");
	strcat(s, fe->name);
	if (lstat(s, &statbuf) != -1)		  
	  if ((statbuf.st_ctime > fe->i.ctime) && (!S_ISDIR(fe->i.mode)))
	      select_entry_engine(&fe->i, NULL, s, 0, 1, 2);
	fe++;
    }
    close_statusbox(mes);
}


int main(int argc, char *argv[]) {

    char     org_dir[MAXNAMLEN];
    int      x;

    init_memory();
    directory = my_malloc(1);			  
    sel_files = my_malloc(1);
    vt_info = my_malloc(1);
    dv=0;
    read_environment();
    read_prefs_file(argc, argv); 
    parse_args(argc, argv);
    fixup_device_names();
    init_buffers();
    open_logfile("Backup");
    init_windows();				 /* Draw windows */
    clear_screen();
    if (check_backup()) {
	if (!append) {				 /* no archive */
	    tdh.archive_id = time(NULL);	 /* set defaults */
	    strcpy(tdh.archive_title, archive_title);
	}
	strcpy(org_dir, cur_dir);		 /* save original cwd */
	if (update) 
	  find_update_files();
	print_title_line();
	x = select_files(cur_dir);		 /* select files */
	delwin(on_vol); delwin(selection); delwin(files);
	my_werase(win_main, COLOR_MAIN); wrefresh(win_main);
	
	if (x)  do_backup(org_dir);
    }
    else {
	delwin(on_vol); delwin(selection); delwin(files);
    }

    close_logfile("Backup");
    my_free_all();
    delwin(title);
    my_werase(win_main, COLOR_MAIN); wrefresh(win_main);
    final_message("Backup");
    werase(win_main); wrefresh(win_main);
    endwin();
    curs_set(1);
    return 0;			                 /* succesful return */
}
