[comp.sources.misc] v02i030: hdscan - interactive directory maintenance utility, Part 1/2

trb@stag.UUCP ( Todd Burkey ) (01/31/88)

Comp.sources.misc: Volume 2, Issue 30
Submitted-By: Todd Burkey <trb@stag.UUCP>
Archive-Name: hdscan/Part1

[WARNING!!!  THIS IS SHAREWARE.  ++bsa]

Here is a little utility you might find interesting. The version included
in this release is my first stab at porting the program over to Unix from
the Atari ST world, so please excuse the strange layout of the code (lots
of things were cut and even more things added to accommodate a non-graphics
environment)...also, my first exposure to curses wasn't all that pleasant.

  -Todd Burkey
  ...ihnp4!meccts!stag!trb

  (compile with cc -O hdscan.c -o hdscan -lcurses -ltermlib)
  (or if sysV: cc -O hdscan.c -o hdscan -lcurses -DNOTBSD)

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-rw-rw-   1 allbery  uucp       65167 Jan  4 02:00 hdscan.c
#
echo 'x - hdscan.c'
if test -f hdscan.c; then echo 'shar: not overwriting hdscan.c'; else
sed 's/^X//' << '________This_Is_The_END________' > hdscan.c
X/* HDSCAN copyright 1987 by Todd Burkey. This is not a Public Domain Package.
X   It is distributed under the shareware policy in order to allow as many
X   people as possible to use it, but if you use it and like it, the author
X   (me) would appreciate a donation of $20 to help further development
X   (and to help convince my wife that my adding a unix box to the mac/st/pc
X   clutter in our house was for more important things than reading news :-) ).
X******************************************************************************
X*DISCLAIMER: This software is provided ASIS. No express or implied warranties*
X*	     are given. The author makes no claim as to the fitness or       *
X*	     applicability of the software for any particular application.   *
X*	     The user uses this code at their own risk.                      *
X******************************************************************************
X
XWHY SHAREWARE?
X   This method of distribution is in lieu of my decision not to market the UNIX
X   version of hdscan as a product (I hate marketting). Shareware is a useful
X   approach, since I want the most people to get access to hdscan while still
X   allowing me to maintain control (and rights) over the code. Shareware works
X   on PC's, but time will tell if it can be extended to the Unix Market. It's a
X   whole lot more fun than buying a product and deciding you don't like it.
X   Have fun.
X   Oh yes...
X
X   Todd Burkey
X   3546 Pilgrim Lane
X   Plymouth, Mn 55441
X
X
XWHAT IS HDSCAN?
X  A directory tree browser, file peruser, backup tool, and more. READ THE
X  DOCS! This program will seem very confusing if you have never used tools
X  like XTREE, 1DIR, etc on PC's.
X
X
XSPECIFIC COPYRIGHT INFORMATION:
X  Feel free to distribute this code ASIS (no code, comment, or documentation
X  changes) via any medium you so desire, just so long as the distribution is
X  not for profit. If you wish changes to be made in the distributed code, feel
X  free to mail suggestions or even specific code changes to the address below.
X  Corporate licensing information is available upon request.
X
X   ...ihnp4!meccts!stag!trb  or
X   trb@stag.UUCP
X
XCOMPILING INFO:
X  What! No Makefile? Nothing fancy in this first release. Just type:
X    cc -O hdscan.c -o hdscan -lcurses -ltermlib
X  Or if you are running sys5r2 then add a -DNOTBSD to the line. Note that other
X  versions of unix may work...I had so much fun learning curses and how
X  opendir/readdir/stat works on BSD and then all over again on sys5 that I
X  really didn't get around to looking at other systems.
X
X
XRevision History:
X  1.00 - First Release (Atari-ST, 1/10/87)
X  1.10 - Bug fixes
X  1.20 - Function key mappings, arc, etc
X  2.00 - First Production version
X  1.21 - PD version with all bugs fixed as in 2.00
X  x.22 - Added hdscan.drv map, dir create, and write verify on/off
X  x.30 - Better Search and copy routines (Atari-ST, 4/20/87)
X  2.90 - Special branch (BSD Unix Only). Lots of new stuff. 10/15/87
X  2.91 - Added Sys5 support. 11-27-87. (added 350 lines)
X  2.99 - First Beta Code Release. 11-28-87. (oh, come on...no bug reports?)
X  3.00 - First Unix Main Release. 11-25-87 (Added single directory rescan,
X	 parallel directory scans, command line options, better directory
X	 orientation, some interrupt recovery.)
X
XPotential Gotcha's:
X  1) Problems due to using fixed arrays:
X     a) scanning too many directories or files will exit program
X     b) directory paths>128 chars or file names>32 chars may cause strange
X	things to happen...this is fixable by changing the constants
X	MYDIRLEN and MYFILELEN
X   2) Speed. HDSCAN has been optimized for speed on the frontend (during the
X      scanning/file info gathering stage.) I scanned a 250MB partition on a SUN
X      that had 14,500 files on it and did notice that HDSCAN ran faster than
X      find (!?!), but some operations like sorting and beautify were dogs...
X      I strongly recommend use of the exclude feature in the HDSCAN.KEY file
X      if you plan on running HDSCAN across massive file systems.
X*/
X
X/*
X   Start of a very messy set of code...the original ST code was written in
X   about 3 weeks, so there are very few comments and the structure is somewhat
X   nauseating in places...it will be improved.
X   IMPORTANT NOTE:
X     I didn't include the high speed sector level recursive FAT table traverse
X     code that I use on the ST. If you think the previous sentence was
X     confusing, you should see the mess that part of the code is in (it was
X     optimized for raw speed and nothing else). Maybe on a future version. This
X     would only be of use to the Super User (root), anyway.
X*/
X#include <ctype.h>
X#include <time.h>
X#include <curses.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#ifndef NOTBSD
X#include <sys/dir.h>
X#endif
X
X#ifdef NOTBSD
X#include <sys/param.h>
X#include <fcntl.h>
Xtypedef unsigned short u_short;
X#ifndef DEV_BSIZE
X#define	DEV_BSIZE	512
X#endif
X#define DIRBLKSIZ	DEV_BSIZE
X#define	MAXNAMLEN	255
X
Xstruct  direct {
X  long  d_ino;			/* inode number */
X  short d_reclen;		/* length of record */
X  short d_namlen;		/* length of d_name */
X  char  d_name[MAXNAMLEN + 1];	/* file name */
X  };
X
X#ifdef DIRSIZ
X#undef DIRSIZ
X#endif
X#define DIRSIZ(dp) ((sizeof (struct direct)-256) + (((dp)->d_namlen+4) &~ 3))
X
Xtypedef struct _dirdesc {
X  int  dd_fd;
X  long dd_loc;
X  long dd_size;
X  char dd_buf[DIRBLKSIZ];
X  } DIR;
X
X#ifndef NULL
X#define NULL 0
X#endif
Xextern	DIR *opendir();
Xextern	struct direct *readdir();
Xextern	void closedir();
X
X#define rewinddir(dirp)	seekdir((dirp), (long)0)
X#endif
X
X/* Modify the next four parameters to suit your uses. You guessed it, there
X   are going to be some big ugly arrays in this code. */
X#define FILEMAX 5000       /* # of files that can be traversed */
X#define DIRMAX 1000        /* # of directories that can be traversed */
X#define MYDIRLEN  128      /* length of max directory path name */
X#define MYFILELEN 32       /* length of max file name...change at risk */
X
X#define MAXFILES     20
X#define PROMPT 1
X#define SILENT 0
X#define BS 8
X#define YES 1
X#define NO 0
X#define ESC 27
X#define TAB 9
X#define HELP '?'
X#define PAGEUP 'K'
X#define PAGEDOWN 'J'
X#define UPARROW 'k'
X#define DOWNARROW 'j'
X#define LEFTARROW 'h'
X#define RIGHTARROW 'l'
X#define ALTU 'U'
X#define ALTT 'T'
X#define ALTC 'c'
X#define BOT 'L'
X#define TOP 'H'
X#define up 0
X#define down 1
X
X
X/* globals */
X
Xchar diff_path[228],unmount_path[228],mount_path[228],backup_path[228];
Xchar copy_path[228],arc_path[228],edit_path[228],comp_path[228],uncomp_path[228];
Xchar fixme[228];
Xchar where_am_i[200];
Xchar tmpname[140];
Xchar dirpath[DIRMAX][MYDIRLEN];
Xchar buffer[4096],buffer2[4096];
Xchar filename[FILEMAX][MYFILELEN];
Xchar skip_path[100][32];
Xchar down_path[50][132];
Xchar tbtmp[228],
X     ans[100],
X     Tagged[FILEMAX],
X     srch[80],
X     orderstr[8],
X     outbuf[140],
X     strans[140],
X     fkeynam[11][128],
X     dirlev[16][70],
X     datstr[12],
X     fnam[MYFILELEN],
X     arcopt[128];
Xint pcnt,z,zz;         /* will hold the FAT table */
Xint do_prompt=1,
X    over_do=0,
X    over_skip=0,
X    multi_flag=0,
X    filecnt=0,
X    dircnt=0,
X    ondir=0,
X    selcnt=0,
X    firsttime=0,
X    diffcnt=0,
X    diffit1;
Xint plen=16,        /* Length of the file display window */
X    pcol1=46,
X    ptop=3,
X    file_sort,
X    better_always=0,
X    sort_always=0,
X    howfar,
X    nasty,
X    retry;
Xint got_int = 0;
Xint got_fixme = 0;
Xint trace_link = 0;
Xint ghost_flag = 0;
Xint xpert_flag = 0;
Xint lastdir[20];
Xint depth[100];
Xint do_cnt,skip_cnt;
Xint max_level;
Xint qans,sortopt,olddir;
Xint sd[DIRMAX];
Xint dirptr[FILEMAX];  /* pointer into array of directory names */
Xint bf[FILEMAX],sf[FILEMAX];
Xunsigned long filesize[FILEMAX],oldsize;
Xlong tmp,lfix();
Xlong int tot_tag=0,htst,tst;
Xlong sel_size=0;
Xlong diskused[17];
Xtime_t tmpd;
Xtime_t file_mdate[FILEMAX],file_adate[FILEMAX],file_cdate[FILEMAX];
Xtime_t old_adate,old_cdate,old_mdate;
Xino_t oldfnum,filefnum[FILEMAX];
Xu_short oldmode,filemode[FILEMAX];
Xshort filefnl[FILEMAX],filefuid[FILEMAX],filefgid[FILEMAX];
Xshort oldfnl,oldfuid,oldfgid;
Xstruct tm *tmdate;
Xchar *getenv();
Xint get_key();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X  commandline(argc, argv);
X  init_fkeys();
X  ws_init();
X  do_fat();
X  mon_keys();
X  terminate();
X  }
X
Xcommandline(argc, argv)
Xchar **argv;
X{
X  int j,max_depth;
X  over_do=0;
X  over_skip=0;
X  do_cnt=0;
X  skip_cnt=0;
X  max_depth=99;
X  if (argc>1) {
X    for (j=1; j<argc; j++) {
X       if (argv[j][0] == '-') {
X	  switch (argv[j][1]) {
X	    case 'g': ghost_flag=1; break;
X            case 'x': xpert_flag=1; break;
X            case 'b': sortopt=3; sort_always=1;better_always=1; break;
X	    case '0': max_depth=0; break;
X	    case 'i': strcpy(skip_path[skip_cnt++],argv[++j]);over_skip=1;break;
X            default:  printf("Unknown Switch: %s\n",argv[j]);
X                      printf("usage:\thdscan [[ -g -x -b -0 -i match -i match ...] dir_path1 dir_path2 ...]");
X                      exit(99);
X            }
X	  }
X       else {
X	  over_do=1;
X	  depth[do_cnt]=max_depth;
X	  strcpy(down_path[do_cnt++],argv[j]);
X	  }
X       }
X     }
X   }
X
Xmon_keys()
X{
X  while((htst=get_key())!='Q') {     /* this started so simple... */
X    got_int=0;
X    tst=0;
X    if(htst=='t') do_tag();
X    if(htst=='u') do_untag();
X    if((htst)=='V') hexdump(z); 
X    if((htst)=='b'||(htst)=='B') { beauty(); goto_top(); }
X    if((htst)=='v') viewfile(z); 
X    if((htst)=='e'||(htst)=='E') { erase_tag(); Refresh(); }
X    if((htst)=='~') create_dir();
X    if((htst)=='!') { re_fat(); show_tag_cnt(); }
X    if((htst)=='*') editfile();
X    if((htst)=='X') {
X      if(xpert_flag) xpert_flag=0;
X      else xpert_flag=1;
X      redraw_scr();
X      }
X    if((htst)=='G') {
X      if(ghost_flag) ghost_flag=0;
X      else ghost_flag=1;
X      redraw_scr();
X      }
X    if((htst=='z')||htst=='Z') do_compress();
X    if((htst)=='#') do_immediate();
X    if((htst)=='+') do_addtoarc();
X    if((htst)=='=') do_peekinarc();
X    if((htst)=='o'||(htst)=='O') { sort(); goto_top(); }
X    if((htst)=='d'||(htst)=='D') {
X      prompt("Really want to delete it? [N]");
X      if(yes_no(NO)==YES) delete();
X      cprompt();
X      Refresh();
X      }
X    if((htst)=='r'||(htst)=='R') {f_rename(); cprompt(); Refresh();}
X    if((htst)=='I') do_info();
X    if((htst)=='s'||(htst)=='S') { select(); goto_top(); }
X    if((htst)=='m') do_masscp();
X    if((htst)=='M') do_massit();
X    if((htst)==TAB) { sel_dir(); goto_top(); }
X    if((htst)==BOT) { tst=0; goto_bot(); }
X    if(htst==PAGEUP) { htst=UPARROW; tst=PAGEUP; }
X    if(htst==PAGEDOWN) { htst=DOWNARROW; tst=PAGEDOWN; }
X    if((htst>='0')&&(htst<='9')) {
X      multi_flag=0;
X      got_fixme=0;
X      do_command(sf[z],(int) (htst-'0'));
X      redraw_scr();
X      }
X    if(htst==HELP) {
X       help();
X       redraw_scr();
X       }
X    if(htst=='c'||htst=='C') diff(z);
X    if(htst==ALTT) {
X       retag();
X       Refresh();
X       }
X    if(htst==ALTU) {
X       for(zz=0;zz<=filecnt;zz++)
X         if(Tagged[zz]=='*'||Tagged[zz]=='#') Tagged[zz]=' ';
X       tot_tag=0;
X       Refresh();
X       }
X    if(htst==TOP) goto_top();
X    if(htst==LEFTARROW) do_pageup();
X    if(htst==RIGHTARROW) do_pagedown();
X    if(htst==DOWNARROW) do_linedown();
X    if(htst==UPARROW) do_lineup();
X    if(htst=='n'||htst=='N'||htst=='p'||htst=='P') do_np_dir();
X    }
X  }
X
Xdo_compress()   /* Warning...this code needs more testing and cleanup */
X{               /* and a rescan of the the file (fstat?) for new size */
X  int status,i;
X  unsigned file1;
X  char fromstr[228];
X  do_fnam(sf[z]);
X  sprintf(fromstr,"%s%s",&dirpath[dirptr[sf[z]]][0],&fnam[0]);
X  if(htst=='Z') {
X    sprintf(tbtmp,"%s %s",&comp_path[0],&fromstr[0]);
X    prompt("Do you REALLY want to compress this? [N]");
X    }
X  else {
X    sprintf(tbtmp,"%s %s",&uncomp_path[0],&fromstr[0]);
X    prompt("Do you REALLY want to uncompress this? [N]");
X    }
X  if(yes_no(NO)==YES) {
X    clear_screen();
X    bprintcr(14,0,"      Invoking compress/uncompress Utility      ");
X    printw("\n\n");
X    refresh();
X    Pexec(PROMPT,tbtmp);
X    if ((file1=open(fromstr,0)) != -1) {
X      status=0;
X      close(file1);
X      }
X    else status= -1;
X    if(status<0) {
X      if(htst=='Z') {
X	strcat(fnam,".Z");
X        strcpy(filename[sf[z]],fnam);
X	}
X      else {
X	i=strlen(fnam);
X	if(i>2) {
X          if(fnam[i-1]=='Z'&&fnam[i-2]=='.') fnam[i-2]='\0';
X          strcpy(filename[sf[z]],fnam);
X	  }
X	}
X      }
X    }
X  redraw_scr();
X  }
X
Xdo_immediate()      /*  this code is new and needs more testing  */
X{
X  do_fnam(sf[z]);
X  sprintf(tbtmp,"%s%s",&dirpath[dirptr[sf[z]]][0],&fnam[0]);
X  prompt("Do you REALLY want to run this? [N]");
X  if(yes_no(NO)==YES) {
X    clear_screen();
X    Pexec(PROMPT,tbtmp);
X    redraw_scr();
X    }
X  else cprompt();
X  }
X
X#ifdef NOTBSD
X/* This is the start of some messy code to handle SysV stuff. */
Xint mkdir(name,mode)
Xchar *name;
Xushort mode;
X{
X  int pid;
X  if((pid=myfork())==0) {
X    close(fileno(stdin));
X    close(fileno(stdout));
X    close(fileno(stderr));
X    open("/dev/null",O_RDWR);
X    dup(fileno(stdin));
X    dup(fileno(stdin));
X    umask(~mode);
X    execl("/bin/mkdir","mkdir",name,(char *)NULL);
X    exit(1);
X    }
X  if(mywait(pid)==0) return(0);
X  return(-1);
X  }
X
Xrename(from,to)
Xchar *from, *to;
X{
X  (void) unlink(to);
X  if (link(from, to) < 0) return -1;
X  (void) unlink(from);
X  return 0;
X  }
X
XDIR *opendir(name)
Xchar *name;
X{
X  register DIR *dirp;
X  register int fd;
X  if ((fd=open(name,0)) == -1) return NULL;
X  if ((dirp=(DIR *)malloc(sizeof(DIR))) == NULL) {
X    close (fd);
X    return NULL;
X    }
X  dirp->dd_fd = fd;
X  dirp->dd_loc = 0;
X  return dirp;
X  }
X
X#define  ODIRSIZ  14
Xstruct  olddirect {
X  short  od_ino;
X  char  od_name[ODIRSIZ];
X  };
X
Xstruct direct *readdir(dirp)
Xregister DIR *dirp;
X{
X  register struct olddirect *dp;
X  static struct direct dir;
X
X  for (;;) {
X    if (dirp->dd_loc == 0) {
X      dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
X      if (dirp->dd_size <= 0) return NULL;
X      }
X    if (dirp->dd_loc >= dirp->dd_size) {
X      dirp->dd_loc = 0;
X      continue;
X      }
X    dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
X    dirp->dd_loc += sizeof(struct olddirect);
X    if (dp->od_ino == 0) continue;
X    dir.d_ino = dp->od_ino;
X    strncpy(dir.d_name, dp->od_name, ODIRSIZ);
X    dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
X    dir.d_namlen = strlen(dir.d_name);
X    dir.d_reclen = DIRSIZ(&dir);
X    return (&dir);
X    }
X  }
X
Xvoid closedir(dirp)
Xregister DIR *dirp;
X{
X  close(dirp->dd_fd);
X  dirp->dd_fd = -1;
X  dirp->dd_loc = 0;
X  free((char *)dirp);
X  }
X
Xvoid seekdir(dirp, loc)
Xregister DIR *dirp;
Xlong loc;
X{
X  long curloc, base, offset;
X  struct direct *dp;
X  extern long lseek();
X  curloc = telldir(dirp);
X  if (loc == curloc) return;
X  base = loc & ~(DIRBLKSIZ - 1);
X  offset = loc & (DIRBLKSIZ - 1);
X  (void) lseek(dirp->dd_fd, base, 0);
X  dirp->dd_loc = 0;
X  while (dirp->dd_loc < offset) {
X    dp = readdir(dirp);
X    if (dp == NULL) return;
X    }
X  }
X
Xlong telldir(dirp)
XDIR *dirp;
X{
X  return lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc;
X  }
X#endif
X
Xdo_tag()    /* Tag currently pointed to file */
X{
X  if(Tagged[sf[z]]=='#'||Tagged[sf[z]]==' ') {
X    tot_tag=tot_tag+filesize[sf[z]];
X    show_tag_cnt();
X    Tagged[sf[z]]='*';
X    highlite(pcnt);
X    }
X  htst=DOWNARROW;
X  }
X
Xdo_untag()    /* Untag currently pointed to file */
X{
X  if(Tagged[sf[z]]=='#'||Tagged[sf[z]]=='*') {
X    if(Tagged[sf[z]]=='*') tot_tag=tot_tag-filesize[sf[z]];
X    show_tag_cnt();
X    Tagged[sf[z]]=' ';
X    highlite(pcnt);
X    }
X  htst=DOWNARROW;
X  }
X
Xdo_addtoarc()     /* this code needs work...I need to get a Unix ARC first */
X{
X  prompt("Add to ARC file. Are You Sure? [N]");
X  if(yes_no(NO)==YES) {
X    do_fnam(sf[z]);
X    sprintf(strans,"ARC PathName [%s]:",&fnam[0]);
X    prompt(strans);
X    tbgetstr(9,5);strcpy(strans,ans);
X    zz=strlen(strans);
X    if(zz<2) {
X      sprintf(strans,"%s%s",&dirpath[dirptr[sf[z]]][0],&fnam[0]);
X      zz=2;
X      }
X    if(zz>1) {
X      sprintf(arcopt,"a %s",&strans[0]);
X      clear_screen();
X      nasty=0;
X      bprintcr(30,0,"ARC'ing Tagged Files");
X      for(zz=0;zz<=filecnt;zz++) {
X         if(Tagged[zz]=='*') arcutil(sf[zz]);
X         if(nasty) break;
X         }
X      redraw_scr();
X      }
X    }
X  cprompt();
X  }
X
Xdo_peekinarc()   /* look inside an arc file. Untested */
X{
X  clear_screen();
X  bprintcr(13,0,"Verbose ARC View (CTRL-S/CTRL-Q To Pause/Continue)");
X  printw("\n");
X  strcpy(arcopt,"v");
X  arcutil(sf[z]);
X  redraw_scr();
X  }
X
Xdo_info()
X{
X  clear_screen();
X  bprintcr(0,0,"                 Disk/Function key Info   (Hit any key to exit)                ");
X  for(zz=0;zz<=16;zz++) diskused[zz]=0;
X  for(zz=0;zz<=filecnt;zz++) {
X    diskused[0]=diskused[0]+filesize[zz];
X    }
X  printw("\n");
X  printw(" |./   %8ld bytes used        |\n",diskused[0]);
X  bprintcr(0,2,"              Function Key Bindings (Override with HDSCAN.KEY)                ");
X  printw("\n");
X  for(zz=0;zz<=9;zz++) printw("       %d:  %s\n",zz,&fkeynam[zz][0]);
X  refresh();
X  get_key();
X  redraw_scr();
X  }
X
Xdo_masscp()    /* copy all tagged files (messy due to backup option) */
X{
X  int back;
X  prompt("Mass Copy: Are You Sure? [N]");
X  if(yes_no(NO)==YES) {
X    prompt("DEST Path [backup unit]: ");
X    tbgetstr(9,5);strcpy(strans,ans);
X    zz=strlen(strans);
X    if(zz==0) {
X      back=1;
X      system(mount_path);
X      strcpy(strans,backup_path);
X      zz=strlen(strans);
X      }
X    else back=0;
X    if(zz==0) {
X      promptw("Sorry, must have a path! [hit c/r]");
X      cprompt();
X      return;
X      }
X    else {
X      if(strans[zz-1]!='/') {
X        strans[zz]='/';
X        strans[zz+1]='\0';
X        }
X      }
X    prompt("Query on Each Copy? [Y]");
X    qans=yes_no(YES);
X    nasty=0;
X    for(zz=0;zz<=filecnt;zz++) {
X      if(got_int) {
X	nasty=1;
X	break;
X	}
X      if(Tagged[zz]=='*') do_copy(zz,strans);
X      if(retry) zz--;
X      if(nasty) break;
X      }
X    }
X  if(back==1) {
X    system("sync");
X    system(unmount_path);
X    }
X  cprompt();
X  Refresh();
X  }
X
Xdo_massit()
X{
X  int i;
X  do {
X    prompt("Enter command (0-9,?) [ESC to exit]:");
X    i=get_key();
X    if(i=='?') do_info();
X    } while(i!=27&&(i<'0'||i>'9'));
X  multi_flag=1;
X  if((i>='0')&&(i<='9')) {
X    prompt("Pause for each file? [Y]");
X    if(yes_no(YES)==NO) do_prompt=0;
X    else do_prompt=1;
X    nasty=0;
X    got_fixme=0;
X    for(zz=0;zz<=filecnt;zz++) {
X      if(got_int) {
X	nasty=1;
X	break;
X	}
X      if(Tagged[zz]=='*') do_command(zz,(int)(i-'0'));
X      if(nasty) break;
X      }
X    redraw_scr();
X    }
X  cprompt();
X  do_prompt=1;
X  multi_flag=0;
X  }
X
Xdo_pageup()      /* Page up one page in the file window */
X{
X  unhigh(pcnt);
X  z=z-plen-1;
X  if(z<0) z=0;
X  if(z<=plen) pcnt=z;
X  upd_scr(z-pcnt,z-pcnt+howfar);
X  highlite(pcnt);
X  }
X
Xdo_pagedown()          /* Page down in the file window */
X{
X  unhigh(pcnt);
X  z=z+plen+1;
X  if(z>(selcnt-howfar)) goto_bot();
X  else {
X    if(z<plen) pcnt=z;
X    upd_scr(z-pcnt,z-pcnt+howfar);
X    highlite(pcnt);
X    }
X  }
X
Xdo_linedown()        /* move down one line in the file window */
X{
X  if(z<selcnt) {
X    unhigh(pcnt);
X    z++;
X    if(pcnt<=plen) pcnt++;
X    else out_it(up);
X    }
X  highlite(pcnt);
X  }
X
Xdo_lineup()     /* move up one line in the file window */
X{
X  if(z>0) {
X    unhigh(pcnt);
X    z--;
X    if(pcnt>0) pcnt--;
X    else out_it(down);
X    }
X  highlite(pcnt);
X  }
X
Xint get_key()  /* simple generic getkey routine... */
X{
X  char c;
X  int result;
X  do {
X    result = read(2, &c, 1);
X    } while (result != 1);
X  return (c & 0177);
X  }
X
Xclear_screen()
X{
X  clear();
X  refresh();
X  }
X
Xcapture_int()
X{
X  signal(SIGINT,capture_int);
X  got_int=1;
X  }
X
Xwsinit2()
X{
X  int terminate();
X  curses_on();
X  signal(SIGINT, capture_int);   /* Still need to bulletproof this */
X  signal(SIGQUIT, terminate);
X  refresh();
X  }
X
Xterminate()
X{
X  move(23, 0);
X  refresh();
X  curses_off();
X  printf("\nThanks for using HDSCAN.\n");
X  exit(0);
X  }
X
Xbad_exit()
X{
X  prompt("Sorry, check FILEMAX & DIRMAX");
X  terminate();
X  }
X
Xbeauty()   /* a somewhat worthless option...for directory grouping */
X{          /* also very crude and slow */
X  register int i,j,cnt;
X  cnt=0;
X  if(htst=='b') better_always=0;
X  if(better_always) prompt("Forcing directory order. Please Wait.");
X  prompt("Ordering by directory. Please Wait.");
X  if(htst=='B') better_always=1;
X  for(i=0;i<=dircnt;i++) {
X    for(j=0;j<=filecnt;j++) {
X      if(dirptr[sf[j]]==sd[i]) bf[cnt++]=sf[j];
X      }
X    }
X  for(i=0;i<=filecnt;i++) sf[i]=bf[i];
X  cprompt();
X  strcpy(orderstr,"BETTER");
X  show_match();
X  }
X
Xre_fat()   /* rescan the file trees...this was more useful on the ST */
X{          /* the 'D' option is a kludge and unnecessary on the ST */
X  char redo_path[228];
X  int cnt,i,oldcnt,tst;
X  prompt("Rescan All or Dir (A/D)?[ESC]");
X  i=get_key();
X  if(i=='A'||i=='a') {
X    oldfnl=oldfuid=oldfgid=9999;
X    oldfnum=9999;
X    oldsize= 9999; old_mdate=old_cdate=old_adate= 9999; olddir= 9999;
X    tot_tag=0;
X    filecnt=0;
X    dircnt=0;
X    ondir=0;
X    sel_size=0;
X    selcnt=0;
X    firsttime=0;
X    diffcnt=0;
X    do_fat();
X    return;
X    }
X  if(i=='D'||i=='d') {
X    cnt=0;
X    strcpy(redo_path,dirpath[dirptr[sf[z]]]);
X    redo_path[strlen(redo_path)-1]='\0';
X    tst=dirptr[sf[z]];
X    for(i=0;i<=filecnt;i++) {
X      if(dirptr[i]!=tst) {
X	if(cnt!=i) {
X	  dirptr[cnt]=dirptr[i];
X	  Tagged[cnt]=Tagged[i];
X	  strcpy(filename[cnt],filename[i]);
X	  filesize[cnt]=filesize[i];
X          file_cdate[cnt]=file_cdate[i];
X          file_adate[cnt]=file_adate[i];
X          file_mdate[cnt]=file_mdate[i];
X          filemode[cnt]=filemode[i];
X          filefuid[cnt]=filefuid[i];
X          filefgid[cnt]=filefgid[i];
X          filefnum[cnt]=filefnum[i];
X          filefnl[cnt]=filefnl[i];
X	  }
X        cnt++;
X        }
X      }
X    filecnt=cnt;
X    oldcnt=dircnt;
X    dircnt=tst-1;
X    ondir=dircnt;
X    max_level=0;
X#ifdef NOTBSD
X    getcwd(where_am_i,228);
X#endif
X#ifndef NOTBSD
X    getwd(where_am_i);
X#endif
X    descend(redo_path,0);
X    chdir(where_am_i);
X    dircnt=oldcnt;
X    sel_size=0;
X    fat_finish();
X    }
X  cprompt();
X  }
X
Xdo_fat()  /* traverse the whole directory tree. */
X{
X  int xx;
X  prompt("Traversing Your Directory Tree...");
X  lastdir[0]=1;
X  max_level=99;
X  if(!do_cnt) descend(".",0);
X  else {
X    for(xx=0;xx<do_cnt;xx++) {
X      max_level=depth[xx];
X#ifdef NOTBSD
X      getcwd(where_am_i,228);
X#endif
X#ifndef NOTBSD
X      getwd(where_am_i);
X#endif
X      descend(down_path[xx],0);
X      chdir(where_am_i);
X      if(got_int) {
X	got_int=0;
X	break;
X	}
X      }
X    }
X  fat_finish();
X  }
X
Xfat_finish()
X{
X  int tmpopt;
X  filecnt--;
X  for(z=0;z<=filecnt;z++) {
X    bf[z]=sf[z]=z;
X    sel_size=sel_size+filesize[z];
X    }
X  for(z=1;z<=dircnt;z++) sd[z]=z;
X  selcnt=filecnt;
X  file_sort=1;
X  if(sort_always) quick(0,selcnt);
X  file_sort=0;
X  tmpopt=sortopt;
X  sortopt=20;
X  quick(1,dircnt);
X  sortopt=tmpopt;
X  show_match();
X  cprompt();
X  if(better_always) beauty();
X  goto_top();
X  prompt("Press ? and I key for more help/info");
X  }
X
Xsort()   /* ahem...this got messier than I intended... */
X{
X  int sortby,ans,ii;
X  prompt("Sort Date/Size/Name/Other (D/S/N/O)? [D]");
X  sortby=get_key();
X  if(sortby==27) {cprompt(); return;}
X  sortby=toupper(sortby);
X  if(sortby!='N'&&sortby!='S'&&sortby!='O') {
X    prompt("Access/Modify/Change time (A/M/C)? [M]");
X    sortby=get_key();
X    if(sortby==27) {cprompt(); return;}
X    sortby=toupper(sortby);
X    if(sortby!='A'&&sortby!='C') sortby='M';
X    }
X  if(sortby=='O') {
X    prompt("Protection/UID/GID/INODE (P/U/G/I)? [P]");
X    sortby=get_key();
X    if(sortby==27) {cprompt(); return;}
X    sortby=toupper(sortby);
X    if(sortby!='U'&&sortby!='G'&&sortby!='I') sortby='P';
X    }
X  if(htst=='O') sort_always=1;
X  else sort_always=0;
X  prompt("Ascending Sort (Y/N) [Y]?");
X  ans=yes_no(YES);
X  for(ii=0;ii<=selcnt;ii++) sf[ii]=bf[ii];
X  sortopt=6;strcpy(orderstr,"-MDATE");
X  if(sortby=='S'&&ans==YES) {sortopt=1;strcpy(orderstr," SIZE ");}
X  if(sortby=='S'&&ans==NO) {sortopt=2;strcpy(orderstr,"-SIZE ");}
X  if(sortby=='N'&&ans==YES) {sortopt=3;strcpy(orderstr," NAME ");}
X  if(sortby=='N'&&ans==NO) {sortopt=4;strcpy(orderstr,"-NAME ");}
X  if(sortby=='M'&&ans==YES) {sortopt=5;strcpy(orderstr,"MDATE ");}
X  if(sortby=='C'&&ans==YES) {sortopt=7;strcpy(orderstr,"CDATE ");}
X  if(sortby=='C'&&ans==NO) {sortopt=8;strcpy(orderstr,"-CDATE");}
X  if(sortby=='A'&&ans==YES) {sortopt=9;strcpy(orderstr,"ADATE ");}
X  if(sortby=='A'&&ans==NO) {sortopt=10;strcpy(orderstr,"-ADATE");}
X  if(sortby=='P'&&ans==YES) {sortopt=11;strcpy(orderstr," MODE ");}
X  if(sortby=='P'&&ans==NO) {sortopt=12;strcpy(orderstr,"-MODE ");}
X  if(sortby=='U'&&ans==YES) {sortopt=13;strcpy(orderstr," UID  ");}
X  if(sortby=='U'&&ans==NO) {sortopt=14;strcpy(orderstr," -UID ");}
X  if(sortby=='I'&&ans==YES) {sortopt=15;strcpy(orderstr," INODE");}
X  if(sortby=='I'&&ans==NO) {sortopt=16;strcpy(orderstr,"-INODE");}
X  if(sortby=='G'&&ans==YES) {sortopt=17;strcpy(orderstr," GID  ");}
X  if(sortby=='G'&&ans==NO) {sortopt=18;strcpy(orderstr," -GID ");}
X  prompt("Sorting with Recursive Quicksort");
X  file_sort=1;
X  quick(0,selcnt);
X  show_match();
X  cprompt();
X  }
X
Xquick(lv,uv)    /* hodgepodge homebrew quick sort...simple version */
Xint lv,uv;
X{
X  register int ii,jj,pivot;
X  if(lv>=uv) return;
X  ii= lv-1;
X  jj= uv;
X  if(file_sort) pivot=sf[jj];
X  else pivot=sd[jj];
X  while(ii<jj) {
X    ++ii;
X    if(file_sort) while(compare(sf[ii],pivot)<0) ++ii;
X    else while(compare(sd[ii],pivot)<0) ++ii;
X    --jj;
X    while(ii<jj) {
X      if(file_sort) {
X	if(compare(sf[jj],pivot)>0) --jj;
X	else break;
X	}
X      else {
X	if(compare(sd[jj],pivot)>0) --jj;
X        else break;
X	}
X      }
X    if(ii<jj) exchange(ii,jj);
X    }
X  jj=uv;
X  exchange(ii,jj);
X  if((ii-lv)<(uv-ii)) {
X    quick(lv,ii-1);
X    quick(ii+1,uv);
X    }
X  else {
X    quick(ii+1,uv);
X    quick(lv,ii-1);
X    }
X  }
X
Xcompare(ii,jj)
Xint ii,jj;
X{
X   switch(sortopt) {
X     case 1: if(filesize[ii]>filesize[jj]) return 1;
X             if(filesize[ii]<filesize[jj]) return -1;
X             else return 0;
X     case 2: if(filesize[ii]>filesize[jj]) return -1;
X             if(filesize[ii]<filesize[jj]) return 1;
X             else return 0;
X     case 3: return strcmp(filename[ii],filename[jj]);
X     case 4: return strcmp(filename[jj],filename[ii]);
X     case 5: if(file_mdate[ii]>file_mdate[jj]) return 1;
X             if(file_mdate[ii]<file_mdate[jj]) return -1;
X             else return 0;
X     case 6: if(file_mdate[ii]>file_mdate[jj]) return -1;
X             if(file_mdate[ii]<file_mdate[jj]) return 1;
X             else return 0;
X     case 7: if(file_cdate[ii]>file_cdate[jj]) return 1;
X             if(file_cdate[ii]<file_cdate[jj]) return -1;
X             else return 0;
X     case 8: if(file_cdate[ii]>file_cdate[jj]) return -1;
X             if(file_cdate[ii]<file_cdate[jj]) return 1;
X             else return 0;
X     case 9: if(file_adate[ii]>file_adate[jj]) return 1;
X             if(file_adate[ii]<file_adate[jj]) return -1;
X             else return 0;
X    case 10: if(file_adate[ii]>file_adate[jj]) return -1;
X             if(file_adate[ii]<file_adate[jj]) return 1;
X             else return 0;
X    case 11: if(filemode[ii]>filemode[jj]) return 1;
X             if(filemode[ii]<filemode[jj]) return -1;
X             else return 0;
X    case 12: if(filemode[ii]>filemode[jj]) return -1;
X             if(filemode[ii]<filemode[jj]) return 1;
X             else return 0;
X    case 13: if(filefuid[ii]>filefuid[jj]) return 1;
X             if(filefuid[ii]<filefuid[jj]) return -1;
X             else return 0;
X    case 14: if(filefuid[ii]>filefuid[jj]) return -1;
X             if(filefuid[ii]<filefuid[jj]) return 1;
X             else return 0;
X    case 15: if(filefnum[ii]>filefnum[jj]) return 1;
X             if(filefnum[ii]<filefnum[jj]) return -1;
X             else return 0;
X    case 16: if(filefnum[ii]>filefnum[jj]) return -1;
X             if(filefnum[ii]<filefnum[jj]) return 1;
X             else return 0;
X    case 17: if(filefgid[ii]>filefgid[jj]) return 1;
X             if(filefgid[ii]<filefgid[jj]) return -1;
X             else return 0;
X    case 18: if(filefgid[ii]>filefgid[jj]) return -1;
X             if(filefgid[ii]<filefgid[jj]) return 1;
X             else return 0;
X    case 20: return strcmp(dirpath[ii],dirpath[jj]);
X    }
X  }
X
Xexchange(ii,jj)
Xint ii,jj;
X{
X  register int swap;
X  if(file_sort) {
X    swap=sf[ii];
X    sf[ii]=sf[jj];
X    sf[jj]=swap;
X    }
X  else {
X    swap=sd[ii];
X    sd[ii]=sd[jj];
X    sd[jj]=swap;
X    }
X  }
X
Xdo_np_dir()     /* simple logic for moving to next/prev directory */
X{
X  int sondir,ii,tcnt;
X  ondir=dirptr[sf[z]];
X  if(htst=='N'||htst=='P') {
X    sondir=1;
X    while(sd[sondir]!=ondir) sondir++;
X    if(htst=='N') sondir++;
X    else sondir--;
X    if(sondir<1) sondir=dircnt;
X    if(sondir>dircnt) sondir=1;
X    do_dir(sondir);
X    }
X  else {
X    ii=z;
X    if(htst=='n') {
X      while(dirptr[sf[ii]]==ondir&&ii<=selcnt) ii++;
X      if(ii>selcnt) return;
X      }
X    else {
X      while(dirptr[sf[ii]]==ondir&&ii>0) ii--;
X      if(ii==0&&dirptr[sf[0]]==ondir) return;
X      }
X    tcnt=pcnt;
X    unhigh(pcnt);
X    pcnt=0;
X    if((tcnt+ii-z)<plen&&(tcnt+ii-z)>=0) {
X      pcnt=tcnt+ii-z;
X      z=ii;
X      highlite(pcnt);
X      }
X    else {
X      z=ii;
X      if(selcnt-ii<plen) pcnt=plen-(selcnt-ii)+1;
X      Refresh();
X      }
X    }
X  }
X
Xdo_dir(dir_num)
Xint dir_num;
X{
X  int ii;
Xretry:
X  sel_size=0;
X  selcnt=0;
X  for(ii=0;ii<=filecnt;ii++) if(sd[dir_num]==dirptr[ii]) {
X    sel_size=sel_size+filesize[ii];
X    sf[selcnt]=bf[selcnt]=ii;
X    selcnt++;
X    }
X  selcnt--;
X  if(selcnt<0&&filecnt>0) {
X    if(htst=='P') dir_num--;
X    else dir_num++;
X    if(dir_num<1) dir_num=dircnt;
X    if(dir_num>dircnt) dir_num=1;
X    goto retry;
X    }
X  srch[0]='D';srch[1]='I';srch[2]='R';srch[3]='\0';
X  file_sort=1;
X  if(sort_always) quick(0,selcnt);
X  else strcpy(orderstr,"RANDOM");
X  show_match();
X  cprompt();
X  goto_top();
X  }
X
Xsel_dir()  /* allows you to view a directory (and all below it) */
X{
X  long keyin;
X  char tnam[33];
X  int sondir,ii,tmpon;
X  ondir=dirptr[sf[z]];
X  sondir=1;
X  while(sd[sondir]!=ondir) sondir++;
Xonce_more:
X  prompt("Use j&k keys, then TAB or A to Select");
X  refresh();
X  while((keyin=get_key())!=TAB) {
X    if(keyin=='A'||keyin=='a') break;
X    if(keyin==DOWNARROW) {
X      sondir++;
X      if(sondir>dircnt) sondir=1;
X      }
X    if(keyin==UPARROW) {
X      sondir--;
X      if(sondir<1) sondir=dircnt;
X      }
X    if(keyin=='/'||keyin=='\\') {
X      prompt("Enter search string:");
X      tbgetstr(9,5);strcpy(tnam,ans);
X      if(strlen(tnam)>0) {
X	tmpon=sondir;
X	do {
X	  if(keyin=='/') {
X	    sondir++;
X	    if(sondir>dircnt) sondir=1;
X	    }
X	  else {
X	    sondir--;
X	    if(sondir<1) sondir=dircnt;
X	    }
X	  } while(tmpon!=sondir&&tbindex(dirpath[sd[sondir]],tnam)<0);
X        if(tmpon==sondir) {
X	  prompt("Sorry, no match found.");
X	  sleep(1);
X	  cprompt();
X	  }
X        }
X      prompt("Use j&k keys, then TAB or A to Select");
X      refresh();
X      }
X    sprintf(tbtmp,"%s                                                                    ",&dirpath[sd[sondir]][0]);
X    sprintf(tbtmp,"%.70s",&tbtmp[0]);
X    printcr(6,1,&tbtmp[0]);
X    refresh();
X    }
X  selcnt=sel_size=0;
X  if((keyin&0xFF)=='A'||(keyin&0xFF)=='a') {
X    for(ii=0;ii<=filecnt;ii++)
X    if(tbindex(dirpath[dirptr[ii]],dirpath[sd[sondir]])==0) {
X      sel_size=sel_size+filesize[ii];
X      sf[selcnt]=bf[selcnt]=ii;
X      selcnt++;
X      }
X    }
X  else {
X    for(ii=0;ii<=filecnt;ii++) if(sd[sondir]==dirptr[ii]) {
X      sel_size=sel_size+filesize[ii];
X      sf[selcnt]=bf[selcnt]=ii;
X      selcnt++;
X      }
X    }
X  selcnt--;
X  if(selcnt<0) {
X    promptw("Nothing found. Hit a key to retry.");
X    goto once_more;
X    }
X  srch[0]='D';srch[1]='I';srch[2]='R';srch[3]='\0';
X  clean_up();
X  }
X
Xshow_match()
X{
X  sprintf(tbtmp,"%.6s",&orderstr[0]);
X  printcr(24,22,tbtmp);
X  sprintf(tbtmp,"        ");
X  printcr(8,22,tbtmp);
X  sprintf(tbtmp,"%.8s",&srch[0]);
X  printcr(8,22,tbtmp);
X  sprintf(tbtmp,"%.5d",selcnt+1);
X  printcr(54,22,tbtmp);
X  sprintf(tbtmp,"%.9ld",sel_size);
X  printcr(68,22,tbtmp);
X  refresh();
X  }
X
Xdo_fnam(it)
X{
X  strcpy(fnam,filename[it]);
X  }
X
Xf_rename()
X{
X  char tnam[33],tostr[100],fromstr[100];
X  if(Tagged[sf[z]]=='X'||Tagged[sf[z]]=='D') return;
X  do_fnam(sf[z]);
X  sprintf(fromstr,"%s%s",&dirpath[dirptr[sf[z]]][0],&fnam[0]);
X  prompt("New name? ");
X  tbgetstr(9,5);strcpy(tnam,ans);
X  if(strlen(tnam)>0) {
X    sprintf(tostr,"%s%s",&dirpath[dirptr[sf[z]]][0],&tnam[0]);
X    if((rename(fromstr,tostr))!=0) {
X      promptw("Error Renaming file! [hit a key]");
X      return;
X      }
X    else strcpy(filename[sf[z]],tnam);
X    }
X  }
X
Xerase_tag()
X{
X  long did;
X  int doit,zz,query;
X  char fromstr[100];
X  prompt("Delete Tagged Files? [y/N]");
X  if(yes_no(NO)==NO) {cprompt();return;}
X  prompt("Query for each file? [Y/n]");
X  if(yes_no(YES)==YES) query=1;
X  else query=0;
X  for(zz=0;zz<=filecnt;zz++) {
X    if(Tagged[zz]=='*') {
X      do_fnam(zz);
X      sprintf(fromstr,"%s%s",&dirpath[dirptr[zz]][0],&fnam[0]);
X      cprompt();
X      sprintf(tbtmp,"DEL->%.34s",&fromstr[0]);
X      bprintcr(1,8,tbtmp);
X      refresh();
X      doit=1;
X      if(query==1) {
X         printcr(5,9,"OK?");
X         if(yes_no(NO)==NO) doit=0;
X         }
X      if(got_int) {
X        got_int=0;
X	promptw("Interrupt Received. Hit a key");
X        goto abort_it;
X        }
X      if(doit==1) {
X        did=unlink(fromstr);
X        if(did!=0) promptw("Sorry, couldn't delete!  [hit a key]");
X        else {
X          tot_tag=tot_tag-filesize[zz];
X          Tagged[zz]='X';
X          }
X        }
X      }
X    }
Xabort_it:
X  cprompt();
X  }
X
Xdelete()
X{
X  long did;
X  char fromstr[100];
X  if(Tagged[sf[z]]=='X'||Tagged[sf[z]]=='D') return;
X  do_fnam(sf[z]);
X  sprintf(fromstr,"%s%s",&dirpath[dirptr[sf[z]]][0],&fnam[0]);
X  did=unlink(fromstr);
X  if(did!=0) promptw("Sorry, couldn't delete!  [hit a key]");
X  else {
X     if(Tagged[sf[z]]=='*') tot_tag=tot_tag-filesize[sf[z]];
X     Tagged[sf[z]]='X';
X     }
X  cprompt();
X  }
X
Xtbgetstr(y,x)         /* added this after the curses gets() kept screwing up */
Xint y,x;
X{
X  int ip,ii;
X  ans[0]='\0';
X  ip= -1;
X  move(y,x);
X  while((ii=get_key())!='\r'&&ii!='\n') {
X    if(ii==BS&&ip<0) continue;
X    ip++;
X    ans[ip]=ii;
X    if(ii==BS) {
X      ip--;
X      if(ip>=0) mvaddch(y,x+ip,' ');
X      move(y,x+ip);
X      ip--;
X      }
X    else mvaddch(y,x+ip,ii);
X    refresh();
X    }
X  ip++;
X  ans[ip]='\0';
X}
X
Xcreate_dir()     /* Quick and dirty create dir command... */
X{
X  int ii;
X  char dirstr[MYDIRLEN],tnam[MYDIRLEN];
X  prompt("Dir path? ");
X  tbgetstr(9,5);strcpy(tnam,ans);
X  if(strlen(tnam)==0) {cprompt(); return;}
X  if(tnam[0]=='~') sprintf(dirstr,"%s%s",&dirpath[dirptr[sf[z]]][0],&tnam[1]);
X  else strcpy(dirstr,tnam);
X  ii=umask(0);
X  if((mkdir(dirstr,0777))!=0) promptw("Sorry, couldn't create!  [hit a key]");
X  ii=umask(ii);
X  cprompt();
X  }
X
Xdiff(it)    /* very, very simple minded file compare... */
Xint it;
X{
X  unsigned numin1,numin2,file1,file2;
X  int ii,howmuch;
X  long lcnt=0;
X  char cfile1[MYDIRLEN],cfile2[MYDIRLEN];
X  if(Tagged[sf[it]]=='X'||Tagged[sf[it]]=='D'||Tagged[sf[it]]=='C') return;
X  if(diffcnt==0) {
X    diffcnt=1;
X    diffit1=sf[it];
X    Tagged[sf[it]]='C';
X    prompt("Now select a file to compare");
X    return;
X    }
X  clear_screen();
X  do_fnam(diffit1);
X  sprintf(cfile1,"%s%s",&dirpath[dirptr[diffit1]][0],&fnam[0]);
X  do_fnam(sf[it]);
X  sprintf(cfile2,"%s%s",&dirpath[dirptr[sf[it]]][0],&fnam[0]);
X  if(htst=='C') {
X    sprintf(tbtmp,"%s %s %s",&diff_path[0],&cfile1[0],&cfile2[0]);
X    Pexec(PROMPT,tbtmp);
X    goto cleanup2;
X    }
X  else {
X    bprintcr(30,0,"Quick File Compare");
X    printw("\n\n");
X    if ((file1=open(cfile1,0)) == -1) {
X       prompt("Error: Cannot Open File! [hit a key]");
X       goto cleanup;
X       }
X    printw("File 1: %s\n",&cfile1[0]);
X    if ((file2=open(cfile2,0)) == -1) {
X       prompt("Error: Cannot Open File! [hit a key]");
X       close(file1);
X       goto cleanup;
X       }
X    printw("File 2: %s\n",&cfile2[0]);
X    refresh();
X    diffcnt=0;
X    do {
X      numin1=read(file1,buffer,4096);
X      numin2=read(file2,buffer2,4096);
X      if(numin1>numin2) howmuch=numin2;
X      else howmuch=numin1;
X      for(ii=0;ii<howmuch;ii++) {
X        lcnt++;
X        if(buffer[ii]!=buffer2[ii]) {
X          diffcnt++;
X          printw("At byte %6ld, File1=%4d (dec) and File2=%4d (dec)\n",
X            lcnt,buffer[ii],buffer2[ii]);
X          if(diffcnt>14) {
X            printw("Found 15 differences...That's enough\n");
X            goto getout;
X            }
X          }
X        }
X      } while(numin1>0 && numin2>0);
X    if(diffcnt==0) printw("No differences found through byte # %ld\n",lcnt);
X    if(numin1>numin2) printw("File 1 is larger than File 2\n");
X    if(numin1<numin2) printw("File 2 is larger than File 1\n");
X    }
Xgetout:
X  close(file1);
X  close(file2);
X  printw("\nHit any key to Return\n");
Xcleanup:
X  refresh(); get_key();
Xcleanup2:
X  Tagged[diffit1]=' ';
X  diffcnt=0;
X  redraw_scr();
X  }
X
Xinit_fkeys()    /* Initialize the bindings for keys 0-9 */
X{
X  char buf[128],tnam[128];
X  char *en;
X  FILE *ifp;
X  int i,j,eqpos,colpos;
X  fixme[0]='\0';
X  strcpy(copy_path,"#");
X  strcpy(backup_path,"/floppy/");
X  strcpy(mount_path,"mount /dev/fd0 /floppy");
X  strcpy(unmount_path,"umount /dev/fd0");
X  strcpy(diff_path,"diff");
X  strcpy(arc_path,"arc");
X  strcpy(edit_path,"vi");
X  strcpy(comp_path,"compress");
X  strcpy(uncomp_path,"uncompress");
X  for(i=0;i<=9;i++) {sprintf(tnam,"key_%1d",i);strcpy(fkeynam[i],tnam);}
X  if((ifp=fopen("HDSCAN.KEY","r"))==NULL) {
X    if((en=getenv("HOME"))!=NULL) {
X      strcpy(tbtmp,en);
X      strcat(tbtmp,"/HDSCAN.KEY");
X      ifp=fopen(tbtmp,"r");
X      }
X    }
X  if(ifp!=NULL) {
X    while(fgets(buf,128,ifp)!=NULL) {
X      for(i=0,j=0,colpos=0,eqpos=0;i<=strlen(buf);i++,j++) {
X        if(buf[j]=='=') eqpos=j;
X	if(buf[j]==':') colpos=j;
X        if(buf[i]=='\n'||buf[i]==' '||buf[i]=='\t') j--;
X	if(buf[i]=='\n'||buf[i]=='\r') buf[i]='\0';
X        }
X      if(eqpos==1) {
X        j=buf[0]-'0';
X        if(j>=0&&j<=9) {
X          strcpy(fkeynam[j],&buf[2]);
X          }
X        }
X      if(colpos==1) {
X	switch(buf[0]) {
X	  case 'G':
X	    if(!ghost_flag) {
X	      if(buf[2]=='Y') ghost_flag=1;
X	      else ghost_flag=0;
X	      }
X	    break;
X          case 'X':
X	    if(!xpert_flag) {
X	      if(buf[2]=='Y') xpert_flag=1;
X	      else xpert_flag=0;
X	      }
X	    break;
X	  case 'A':
X	    strcpy(arc_path,&buf[2]);
X	    break;
X	  case 'E':
X	    strcpy(edit_path,&buf[2]);
X	    break;
X          case 'D':
X	    strcpy(diff_path,&buf[2]);
X	    break;
X          case 'M':
X	    strcpy(mount_path,&buf[2]);
X	    break;
X	  case 'U':
X	    strcpy(unmount_path,&buf[2]);
X	    break;
X	  case 'u':
X	    strcpy(uncomp_path,&buf[2]);
X	    break;
X	  case 'C':
X	    strcpy(copy_path,&buf[2]);
X	    break;
X	  case 'B':
X	    strcpy(backup_path,&buf[2]);
X	    break;
X	  case 'c':
X	    strcpy(comp_path,&buf[2]);
X	    break;
X	  case '-':
X	    if(!over_skip) {
X	      strcpy(skip_path[skip_cnt],&buf[2]);
X	      skip_cnt++;
X	      }
X	    break;
X	  case '+':
X	    if(!over_do) {
X	      if(buf[2]>='0'&&buf[2]<='9') {
X	        depth[do_cnt]=buf[2]-'0';
X	        strcpy(down_path[do_cnt],&buf[3]);
X	        }
X	      else {
X	        depth[do_cnt]=99;
X	        strcpy(down_path[do_cnt],&buf[2]);
X	        }
X	      do_cnt++;
X	      }
X	    break;
X          }
X	}
X      }
X    fclose(ifp);
X    }
X  }
X
Xdo_command(zf,fkey) /* Note: No error processing is handled. Also, I used */
Xint zf,fkey;        /* system() instead of execv, since I don't need speed. */
X{
X  int ii,j,m,i,k,jj;
X  char command[228],lprefix[228],prefix[200],fromstr[228];
X  if(Tagged[zf]=='X'||Tagged[zf]=='D') return;
X  clear_screen();
X  do_fnam(zf);
X  sprintf(fromstr,"%s%s",&dirpath[dirptr[zf]][0],&fnam[0]);
X  strcpy(prefix,fnam);
X  ii=strlen(prefix);
X  for(j=ii;j>0;j--) {
X    if(prefix[j]=='.') {
X      prefix[j]='\0';
X      break;
X      }
X    }
X  strcpy(lprefix,fromstr);
X  ii=strlen(lprefix);
X  for(j=ii;j>0;j--) {
X    if(lprefix[j]=='.') {
X      lprefix[j]='\0';
X      break;
X      }
X    }
X  k=0;
X  if(got_fixme==0) {
X    if(fkeynam[fkey][0]=='<') {
X      got_fixme=1;
X      sprintf(tbtmp,"Command string:[%s]",&fixme[0]);
X      prompt(tbtmp);
X      tbgetstr(9,5);
X      if(strlen(ans)>0) strcpy(fixme,ans);
X      }
X    else strcpy(fixme,fkeynam[fkey]);
X    }
X  ii=strlen(fixme);
X  for(i=0;i<ii;i++) {
X    if(fixme[i]=='?'&&i==0) continue;
X    if(fixme[i]=='@') {
X      i++;
X      switch(fixme[i]) {
X	case 'f':
X	  jj=strlen(fnam);
X	  for(m=0;m<jj;m++) command[k++]=fnam[m];
X	  break;
X	case 'p':
X	  jj=strlen(prefix);
X	  for(m=0;m<jj;m++) command[k++]=prefix[m];
X	  break;
X	case 'F':
X	  jj=strlen(fromstr);
X	  for(m=0;m<jj;m++) command[k++]=fromstr[m];
X	  break;
X	case 'P':
X	  jj=strlen(lprefix);
X	  for(m=0;m<jj;m++) command[k++]=lprefix[m];
X      }
X      }
X    else command[k++]=fixme[i];
X    }
X  command[k]='\0';
X  sprintf(tbtmp,"Invoking %s",command);
X  bprintcr(13,0,tbtmp);
X  standend();
X  printcr(0,1," ");       /* don't ask...late at night and reverse is sticky */
X  addch('\n');addch('\r');
X  refresh();
X  if(fkeynam[fkey][0]=='?') {
X    printf("Are you sure you want to do this? [N]");
X    refresh();
X    if(yes_no(NO)==NO) {redraw_scr(); return; }
X    addch('\n');addch('\r');
X    refresh();
X    }
X  curses_off();
X  system(command);
X  if(do_prompt==1) {
X    printf("       [Hit RETURN to Continue, Q and RETURN to Abort]       \n");
X    i=get_key();
X    if(i=='Q'||i=='q') nasty=1;
X    }
X  curses_on();
X  }
X
Xeditfile()
X{
X  char fromstr[100];
X  if(Tagged[sf[z]]=='X'||Tagged[sf[z]]=='D') return;
X  clear_screen();
X  sprintf(tbtmp,"Invoking %s",&edit_path[0]);
X  bprintcr(13,0,tbtmp);
X  refresh();
X  do_fnam(sf[z]);
X  sprintf(fromstr,"%s %s%s",&edit_path[0],&dirpath[dirptr[sf[z]]][0],&fnam[0]);
X  Pexec(PROMPT,fromstr);
X  redraw_scr();
X  }
X
Xcurses_off()
X{
X  nl();
X  echo();
X  nocrmode();
X  standend();
X  endwin();
X  }
X
Xcurses_on()
X{
X  initscr();
X  nonl();
X  noecho();
X  crmode();
X  }
X
Xint Pexec(loudness,cline)
Xint loudness;
Xchar *cline;
X{
X  int stat,i,j,ii,c_cnt;
X  char *c[30];
X  char pname[228];
X  ii=strlen(cline);
X  c_cnt=0;
X  c[c_cnt]=cline;
X  for(j=0;j<ii;j++) {
X    if(cline[j]==' ') {
X      cline[j]='\0';
X      if(c_cnt==0) {
X	for(i=j;i>=0;i--) {
X	  if(cline[i]=='/'||cline[i]=='~') break;
X	  }
X	strcpy(pname,&cline[i+1]);
X        }
X      c_cnt++;
X      c[c_cnt]= &cline[j+1];
X      }
X    }
X  if(c_cnt==0) strcpy(pname,cline);
X  c_cnt++;
X  c[c_cnt]=NULL;
X  if (fork()) wait(&stat);
X  else {
X    if(loudness==PROMPT) {
X      curses_off();
X      }
X    execvp(pname,c);
X    printf("\n\rSorry. Error running %s. Is it missing?\n",&pname[0]);
X    exit(-1);
X    }
X  if(loudness==PROMPT) {
X    printf("        [Hit RETURN to Continue, Q and RETURN to Abort]       \n");
X    i=get_key();
X    if(i=='Q'||i=='q') nasty=1;
X    curses_on();
X    }
X  return(stat);
X  }
X
Xarcutil(it)
Xint it;
X{
X  int status;
X  char fromstr[228];
X  if(Tagged[it]=='X'||Tagged[it]=='D') return;
X  do_fnam(it);
X  sprintf(fromstr,"%s %s %s%s",&arc_path[0],&arcopt[0],&dirpath[dirptr[it]][0],&fnam[0]);
X  printf("%s\n",&fromstr[0]);
X  status=Pexec(PROMPT,fromstr);
X  if(status<0) nasty=1;
X  else {
X    if(strlen(arcopt)<3){
X      printf("--Hit RETURN to continue--\n");
X      get_key();
X      }
X    }
X  }
X
Xviewfile(it)  /* simple view...you can always use more or less */
Xint it;
X{
X  unsigned i,numin,file;
X  int cnt,chcnt=0;
X  char fromstr[200];
X  if(Tagged[sf[it]]=='X'||Tagged[sf[it]]=='D') return;
X  do_fnam(sf[it]);
X  sprintf(fromstr,"%s%s",&dirpath[dirptr[sf[it]]][0],&fnam[0]);
X  if ((file=open(fromstr,0)) == -1) {
X     promptw("Error: Can't Open File! [hit a key]");
X     cprompt();
X     highlite(pcnt);
X     return;
X     }
X  clear_screen();
X  cnt=0;
X  chcnt=0;
X  while((numin=read(file,buffer,4096))>0) {
X     for(i=0;i<numin;i++) {
X        if(cnt==0&&chcnt==0) {
X          bprintcr(15,0,"Hit any key to continue, ESC to Exit");
X	  addch('\n');addch('\r');
X          refresh();
X          }
X        if((buffer[i]>=' '&&buffer[i]<0x7f)||buffer[i]=='\n'||buffer[i]=='\r')
X          addch(buffer[i]);
X        if(buffer[i]=='\n') {addch('\r');refresh();chcnt=0;cnt++;}
X        else {
X          chcnt++;
X          if(chcnt>=79) {
X             chcnt=0;
X             cnt++;
X             addch('\n');addch('\r');
X	     refresh();
X             }
X          }
X        if(cnt==23) {
X	  refresh();
X          cnt=0;chcnt=0;
X          if(get_key()==ESC) {close(file); redraw_scr(); return;}
X          clear_screen();
X          }
X        }
X     }
X   bprintcr(70,0,"-EOF-");
X   refresh();
X   get_key();
X   close(file);
X   redraw_scr();
X   }
X
Xhelp()
X{
X  clear_screen();
X  bprintcr(0,0,"                     Extended HELP for HDSCAN Professional                     ");
X  mvaddstr(1,0," |k/j keys:   Scroll File list up/down  |h/l keys:  Page File list up/down   |");
X  mvaddstr(2,0," |H/L keys:   Go to Top/Bottom of List  |Z/z keys:  Compress/Uncompress file |");
X  mvaddstr(3,0," |G/X keys:   Ghost/eXpert mode toggle  |0-9 keys:  Run external programs    |");
X  mvaddstr(4,0," |t/u keys:   Tag/Untag a file          |N/P keys:  Select Next/Prev Dir     |");
X  mvaddstr(5,0," |U key:      Untag all Tagged files    |T key:     Retag all Copied files   |");
X  mvaddstr(6,0," |m/M keys:   Mass copy/doto all tagged |s/S keys:  Set match on all/Subset  |");
X  mvaddstr(7,0," |V key:      View a file in HEX/ASCII  |v key:     View a file/strings-like |");
X  mvaddstr(8,0," |e key:      Erase all Tagged files    |d key:     Delete current file      |");
X  mvaddstr(9,0," |R key:      Change name               |# key:     Invoke current program   |");
X  mvaddstr(10,0," |= key:      Verbose Arc listing       |+ key:     Add Tagged files to .arc |");
X  mvaddstr(11,0," |TAB key:    Select files by directory |~ key:     Create a directory       |");
X  mvaddstr(12,0," |?/I keys:   HELP/keymap info          |! key:     Rescan drives/Directory  |");
X
X  mvaddstr(13,0," |c/C keys:   Compare/Diff two files    |o/O keys:  Order current/continuous |");
X  bprintcr(0,14,"                                                                               ");
X  mvaddstr(16,0,"HDSCAN+ (c) 1987 by Todd Burkey is a hard disk utility and, if used with care,");
X  mvaddstr(17,0,"should prove an invaluable tool. Todd Burkey and Mindtools will not be held");
X  mvaddstr(18,0,"responsible for any damage, loss of time, etc that this product may incur. As");
X  mvaddstr(19,0,"with any disk utility, use it at your own risk. Please read the doc file!");
X  mvaddstr(20,0,"NOTE: This is not a Public Domain package. It is Shareware and copyrighted. If");
X  mvaddstr(21,0,"you use it and really like it, please send in $20 to become a registered user.");
X  bprintcr(0,23,"                          --Hit any key to continue--                          ");
X  refresh();
X  get_key();
X  }
X
Xhexdump(it)
Xint it;
X{
X  register int nn;
X  unsigned i,numin,tot,file,cfrom;
X  int cnt=0;
X  char fromstr[100];
X  if(Tagged[sf[it]]=='X'||Tagged[sf[it]]=='D') return;
X  tot=0;
X  do_fnam(sf[it]);
X  sprintf(fromstr,"%s%s",&dirpath[dirptr[sf[it]]][0],&fnam[0]);
X  if((file=open(fromstr,0)) == -1) {
X    promptw("Error: Can't Open File! [hit a key]");
X    cprompt();
X    highlite(pcnt);
X    return;
X    }
X  clear_screen();
X  do { /*      read and dump 4k at a time      */
X    for(nn=0;nn<=4095;buffer[nn++]=0);
X    numin=read(file,buffer,4096);
X    if (numin == -1) {
X      promptw("Error: Cannot Read File! [hit a key]");
X      close(file);
X      redraw_scr();
X      return;
X      }
X    cfrom=0;
X    while (cfrom < numin)  {   /*      print the offset in hex */
X      if(cnt==0) {
X        bprintcr(15,0,"Hit any key to continue, ESC to Exit");
X        refresh();
X	addch('\n');addch('\r');
X        }
X      ohw(cfrom+tot);
X      addch(' ');
X                                       /*  print 16 bytes in hex   */
X      for (i=0; i < 16; i++) {
X        addch(' ');
X        ohb(buffer[cfrom++]);
X        }
X      cfrom-=16;
X      addch(' ');
X      addch(' ');
X      addch('|');
X                                         /* print the bytes in ascii */
X      for (i=0; i < 16; i++) {
X          addch((buffer[cfrom]>=' '&&buffer[cfrom]<0x7f)?buffer[cfrom]: '.');
X          cfrom++;
X        }
X      addch('|');
X      cnt++;
X      if(cnt==23) {
X        cnt=0;
X        refresh();
X	if(get_key()==ESC) {close(file); redraw_scr(); return;}
X        clear_screen();
X        }
X      else {
X	addch('\n');
X	addch('\r'); refresh();
X	}
X      }
X    tot+=numin;
X    }
X  while (numin == 4096);
X  if(cnt>0){
X    bprintcr(70,0,"-EOF-");
X    refresh();
X    get_key();
X    clear_screen();
X    }
X  close(file);
X  redraw_scr();
X  }
X
Xohw(wrd)  /*      print a word in hex     */
Xunsigned wrd;
X{
X  ohb(wrd>>8);
X  ohb(wrd);
X  }
X
Xohb(byt)    /* print a byte in hex */
Xint byt;
X{
X  onib(byt>>4);
X  onib(byt);
X  }
X
Xonib(nib)   /* print a nibble in hex */
Xint nib;
X{
X  nib&=15;
X  addch((nib >= 10) ? nib-10+'A': nib+'0');
X  }
X
Xretag()  /* Retag all # files */
X{
X   int ii;
X   for(ii=0;ii<=filecnt;ii++) {
X     if(Tagged[ii]=='#') {
X       Tagged[ii]='*';
X       tot_tag=tot_tag+filesize[ii];
X       }
X     }
X   }
X
Xselect()    /* select by character string...simple and easy */
X{
X   int tmpcnt,totcnt,ii,tmpz,subz;
X   totcnt=tmpcnt=selcnt;
X   sel_size=0;
Xtryagain:
X   selcnt=0;
X   prompt("Match (<=8 chars) [Return for all]? ");
X   tbgetstr(9,5);strcpy(srch,ans);
X   if(strlen(srch)==0) {
X      strcpy(srch,"ALL");
X      for(tmpz=0;tmpz<=filecnt;tmpz++) {
X	bf[tmpz]=sf[tmpz]=tmpz;
X	sel_size=sel_size+filesize[tmpz];
X	}
X      selcnt=filecnt;
X      goto getout;
X      }
X   if((strlen(srch)==1)&&(srch[0]=='*')) {
X      for(tmpz=0;tmpz<=filecnt;tmpz++) {
X         if(Tagged[tmpz]=='*') {
X	   sel_size=sel_size+filesize[tmpz];
X	   bf[selcnt]=sf[selcnt]=tmpz;selcnt++;
X	   }
X         }
X      selcnt--;
X      goto getout;
X      }
X   for(ii=1;ii<=strlen(srch);ii++) {
X     if(srch[ii]=='*'||srch[ii]=='?') {
X       promptw("Sorry, no wild card chars...OK?");
X       goto tryagain;
X       }
X     }
X   if(htst=='s') totcnt=filecnt;
X   for(tmpz=0;tmpz<=totcnt;tmpz++){
X     if(htst=='S') subz=sf[tmpz];
X     else subz=tmpz;
X     if(tbindex(filename[subz],srch)>=0) {
X       sel_size=sel_size+filesize[subz];
X       sf[selcnt]=bf[selcnt]=subz;
X       selcnt++;
X       }
X     }
X   selcnt--;
X   if(selcnt<0) {
X     selcnt=tmpcnt;
X     prompt("Sorry, no matches. Try again [Y]?");
X     if(yes_no(YES)==YES) goto tryagain;
X     }
Xgetout:
X   clean_up();
X   }
X
Xclean_up()
X{
X  cprompt();
X  file_sort=1;
X  if(sort_always) quick(0,selcnt);
X  else strcpy(orderstr,"RANDOM");
X  show_match();
X  if(better_always) beauty();
X  }
X
Xint min(i,j)
Xint i,j;
X{
X  if(i<j) return(i);
X  else return j;
X  }
X
Xupd_scr(f_z,t_z)
Xint f_z,t_z;
X{
X   int ii,jj,tmpz=0;
X   for(ii=f_z;ii<=t_z;ii++) {
X     if(tmpz<=howfar) {
X        sprintf(tbtmp,"%s                                 ", filename[sf[ii]]);
X        sprintf(tbtmp,"%.32s",&tbtmp[0]);
X        printcr(pcol1,ptop+tmpz,tbtmp);
X        mvaddch(ptop+tmpz,pcol1-2,Tagged[sf[ii]]);
X        refresh();
X        }
X     else {
X        printcr(pcol1,ptop+tmpz,"                                ");
X        printcr(pcol1-2,ptop+tmpz," ");
X        }
X     tmpz++;
X     }
X  }
X
XRefresh()
X{
X   howfar=min(plen+1,selcnt);
X   upd_scr(z-pcnt,z-pcnt+plen+1);
X   show_tag_cnt();
X   highlite(pcnt);
X   }
X
Xgoto_top()
X{
X   howfar=min(plen+1,selcnt);
X   unhigh(pcnt);
X   upd_scr(0,plen+1);
X   z=0;
X   pcnt=0;
X   highlite(pcnt);
X   }
X
Xgoto_bot()
X{
X   int tmpz=0;
X   howfar=min(plen+1,selcnt);
X   unhigh(pcnt);
X   upd_scr(selcnt-howfar,selcnt-howfar+plen);
X   z=selcnt;
X   pcnt=howfar;
X   highlite(pcnt);
X   }
X
Xprompt(string)
Xchar *string;
X{
X  cprompt();
X  bprintcr(1,8,string);
X  bprintcr(1,9,"-->");
X  refresh();
X  }
X
Xcprompt()
X{
X  move(8,1);
X  bprintcr(1,8,"                                           ");
X  printcr(1,9,"                                          ");
X  refresh();
X  }
X
Xpromptw(string)
Xchar *string;
X{
X  prompt(string);
X  get_key();
X  }
X
Xint yes_no(yn)
Xint yn;
X{
X  int cin;
X  cin=get_key();
X  if(yn==YES) {
X    if(cin=='n'||cin=='N') return(NO);
X    else return(YES);
X    }
X  if(yn==NO) {
X    if(cin=='y'||cin=='Y') return(YES);
X    else return(NO);
X    }
X  }
X
Xdo_copy(it,string)  /* some last minute changes to support /bin/cp or Apollo */
Xint it;             /* /com/cpf may have made this a bit unstable...*/
Xchar *string;
X{
X    int dummy;
X    int f1,f2;
X    long nn;
X    int q;
X    char buf[16384],fromstr[100],tostr[100];
X    q=NO;retry=0;
X    do_fnam(it);
X    if(strcmp(string,&dirpath[dirptr[it]][0])==0) {
X      promptw("Error! Dest and Source Paths Same - OK?\n");
X      nasty=1;
X      return;
X      }
X    sprintf(fromstr,"%s%s",&dirpath[dirptr[it]][0],&fnam[0]);
X    sprintf(tostr,"%s%s",string,&fnam[0]);
X    cprompt();
X    sprintf(tbtmp,"%.37s",&fromstr[0]);
X    prompt(tbtmp);
X    if(qans==YES) {printcr(5,9,"OK?");refresh();q=yes_no(YES);}
X    if((qans==NO)||(q==YES)) {
X      if(copy_path[0]!='#') {
X	sprintf(tbtmp,"%s %s %s",&copy_path[0],&fromstr[0],&tostr[0]);
X	if(system(tbtmp)==0) goto copy_done;
X	if(tbindex(tostr,backup_path)==0) {
X          unlink(tostr);
X	  system(unmount_path);
X          prompt("Full! New Disk Inserted?  [ESC aborts]");
X          if(get_key()==ESC) nasty=1;
X          else {
X	    redraw_scr();
X            retry=1;
X            system(mount_path);
X	    }
X          }
X        else {
X	  promptw("System copy error: hit [C/R]");
X          redraw_scr();
X          nasty=1;
X          }
X	return;
X	}
X      if((f1=open(fromstr,0)) <0) {
X         sprintf(tbtmp,"Can't Open: %s [C/R]",&fnam[0]);
X	 promptw(tbtmp);
X         nasty=1;
X         return;
X         }
X#ifdef NOTBSD
X      lockf(f1,2,1);
X#endif
X#ifndef NOTBSD
X      flock(f1,2);   /* lock the file so we can't erase it */
X#endif
X      dummy=(int)(filemode[it]&0777);
X      if((f2=open(tostr,0)) >=0) {
X#ifdef NOTBSD
X        if(lockf(f2,2,1)<0) {
X#endif
X#ifndef NOTBSD
X	if(flock(f2,6)<0) {
X#endif
X	  promptw("Can't read/write same file!");
X	  close(f1);
X	  close(f2);
X	  nasty=1;
X	  return;
X	  }
X#ifdef NOTBSD
X        lockf(f2,0);
X#endif
X#ifndef NOTBSD
X        flock(f2,8);
X#endif
X        close(f2);
X	}
X      if((f2=creat(tostr,dummy)) <0) {
X         sprintf(tbtmp,"Can't create: %s [C/R]",&fnam[0]);
X         promptw(tbtmp);
X         close(f1);
X         nasty=1;
X         return;
X         }
X#ifdef NOTBSD
X      lockf(f1,0);
X#endif
X#ifndef NOTBSD
X      flock(f1,8);
X#endif
X      if((f1>=0)&&(f2>=0)) {
X        while((nn=read(f1,buf,16384L))>0) {
X          if(write(f2,buf,nn)!=nn) {
X            close(f1);close(f2);
X            unlink(tostr);
X	    if(tbindex(tostr,backup_path)==0) {
X	      system(unmount_path);
X              prompt("Full! New Disk Inserted?  [ESC aborts]");
X              if(get_key()==ESC) nasty=1;
X              else {
X		retry=1;
X		system(mount_path);
X		}
X              }
X            else {
X              promptw("Error Writing File (Disk Full?)");
X              nasty=1;
X              }
X            return;
X            }
X          }
Xcopy_done:
X        tot_tag=tot_tag-filesize[it];
X        Tagged[it]='#';      /* Show it has been copied */
X        show_tag_cnt();
X        }
X      if(copy_path[0]=='#') {
X	close(f1);
X	close(f2);
X	}
X      }
X    }
X
Xshow_tag_cnt()
X{
X  sprintf(tbtmp,"%.8ld          ",tot_tag);
X  sprintf(tbtmp,"%.8s",tbtmp);
X  printcr(34,3,tbtmp);
X  refresh();
X  }
X
Xunhigh(it)
Xint it;
X{
X  if(selcnt>=0) {
X  if(ghost_flag==0) {
X    sprintf(tbtmp,"%s                                ", filename[sf[z]]);
X    sprintf(tbtmp,"%.32s",&tbtmp[0]);
X    printcr(pcol1,ptop+it,tbtmp);
X    }
X  sprintf(tbtmp,"%c",Tagged[sf[z]]);
X  printcr(pcol1-2,ptop+it,tbtmp);
X  if(ghost_flag==0) standout();
X  mvaddch(ptop+it,pcol1-1,' ');
X  if(ghost_flag==0) standend();
X  refresh();
X  }
X  }
X
Xhighlite(it)
Xint it;
X{
X  if(selcnt>=0) {
X    sprintf(tbtmp,"%s                                ", filename[sf[z]]);
X    sprintf(tbtmp,"%.32s",&tbtmp[0]);
X    if(ghost_flag) printcr(pcol1,ptop+it,tbtmp);
X    else bprintcr(pcol1,ptop+it,tbtmp);
X    if(tst!=PAGEUP&&tst!=PAGEDOWN) file_upd();
X    sprintf(tbtmp,"%c",Tagged[sf[z]]);
X    printcr(pcol1-2,ptop+it,tbtmp);
X    standout();
X    mvaddch(ptop+it,pcol1-1,'>');
X    standend();
X    refresh();
X    }
X  }
X
X/* Update directory, file size, and path info */
Xfile_upd()
X{
X  if(olddir!=dirptr[sf[z]]) {
X    olddir=dirptr[sf[z]];
X    sprintf(tbtmp,"%.70s                                                                     ",&dirpath[olddir][0]);
X    sprintf(tbtmp,"%.70s",&tbtmp[0]);
X    printcr(6,1,&tbtmp[0]);
X    }
X  if(oldsize!=filesize[sf[z]]) {
X    oldsize=filesize[sf[z]];
X    sprintf(tbtmp,"%.8ld        ",oldsize);
X    sprintf(tbtmp,"%.8s",&tbtmp[0]);
X    printcr(7,3,&tbtmp[0]);
X    }
X  if(xpert_flag) {
X    if(oldfgid!=filefgid[sf[z]]) {
X      oldfgid=filefgid[sf[z]];
X      sprintf(tbtmp,"%7d     ",oldfgid);
X      sprintf(tbtmp,"%.7s",&tbtmp[0]);
X      printcr(6,16,&tbtmp[0]);
X      }
X    if(oldfuid!=filefuid[sf[z]]) {
X      oldfuid=filefuid[sf[z]];
X      sprintf(tbtmp,"%7d      ",oldfuid);
X      sprintf(tbtmp,"%.7s",&tbtmp[0]);
X      printcr(6,15,&tbtmp[0]);
X      }
X    if(oldfnl!=filefnl[sf[z]]) {
X      oldfnl=filefnl[sf[z]];
X      sprintf(tbtmp,"%7d      ",oldfnl);
X      sprintf(tbtmp,"%.7s",&tbtmp[0]);
X      printcr(6,14,&tbtmp[0]);
X      }
X    if(oldfnum!=filefnum[sf[z]]) {
X      oldfnum=filefnum[sf[z]];
X      sprintf(tbtmp,"%7ld        ",oldfnum);
X      sprintf(tbtmp,"%.7s",&tbtmp[0]);
X      printcr(6,13,&tbtmp[0]);
X      }
X    if(oldmode!=filemode[sf[z]]) {
X      oldmode=filemode[sf[z]];
X      sprintf(tbtmp,"%7o       ",oldmode);
X      sprintf(tbtmp,"%.7s",&tbtmp[0]);
X      printcr(6,12,&tbtmp[0]);
X      }
X    }
X  if(old_mdate!=file_mdate[sf[z]]) {
X    old_mdate= file_mdate[sf[z]];
X    tmdate=localtime(&file_mdate[sf[z]]);
X    sprintf(datstr,"%.2d/%.2d/%.2d",tmdate->tm_mon+1,tmdate->tm_mday,tmdate->tm_year);
X    printcr(6,6,&datstr[0]);
X    }
X  if(old_adate!=file_adate[sf[z]]) {
X    old_adate= file_adate[sf[z]];
X    tmdate=localtime(&file_adate[sf[z]]);
X    sprintf(datstr,"%.2d/%.2d/%.2d",tmdate->tm_mon+1,tmdate->tm_mday,tmdate->tm_year);
X    printcr(20,6,&datstr[0]);
X    }
X  if(old_cdate!=file_cdate[sf[z]]) {
X    old_cdate= file_cdate[sf[z]];
X    tmdate=localtime(&file_cdate[sf[z]]);
X    sprintf(datstr,"%.2d/%.2d/%.2d",tmdate->tm_mon+1,tmdate->tm_mday,tmdate->tm_year);
X    printcr(34,6,&datstr[0]);
X    }
X  refresh();
X  }
X
Xtbindex(s,t)  /* find pos of t in s */
Xchar s[],t[];
X{
X  int i,j,k;
X  for(i=0;s[i]!='\0';i++) {
X    for(j=i,k=0;t[k]!='\0' && s[j]==t[k];j++,k++);
X    if(t[k]=='\0') return(i);
X    }
X  return(-1);
X  }
X
Xws_init()
X{
X  srch[0]='A';srch[1]='L';srch[2]='L';srch[3]='\0';
X  strcpy(orderstr,"RANDOM");
X  wsinit2();
X  draw_scr();
X  }
X
Xredraw_scr()
X{
X  draw_scr();
X  Refresh();
X  }
X
Xdraw_scr()
X{
X  int i;
X  oldmode=9999;
X  oldfnl=oldfuid=oldfgid=9999;
X  oldfnum=9999;
X  oldsize= 9999; old_mdate=old_cdate=old_adate= 9999; olddir= 9999;
X  clear_screen();
X  standout();
X  if (ghost_flag==0) {
X    for(i=0;i<23;i++) {
X      mvaddch(i,0,' ');
X      if(xpert_flag==0) {if(i>13) mvaddch(i,26,' ');}
X      if(i>1&&i<22) {
X        mvaddch(i,45,' ');
X        mvaddch(i,43,' ');
X        }
X      mvaddch(i,78,' ');
X      }
X    printcr(43,22,"                                    ");
X    printcr(0,0,"                                                                               ");
X    }
X  sprintf(tbtmp," Match= ALL      Order= RANDOM                 Found: %.5d #Bytes: %.9ld  ",filecnt+1,sel_size);
X  printcr(0,22,tbtmp);
X  printcr(0,23,"  HDSCAN by Todd Burkey (c)1987 Mindtools, 3546 Pilgrim Ln, Plymouth MN 55441  ");
X  standend();
X  show_match();
X  if (ghost_flag==0&&xpert_flag==0) {
X   bprintcr(1,14,"     HDSCAN BRIEF HELP    ");
X   printcr(1,15,"t)ag File");
X   printcr(1,16,"u)ntag File");
X   printcr(1,17,"m)ass copy");
X   printcr(1,18,"V)ex Dump");
X   printcr(1,19,"v)iew File");
X   printcr(1,20,"o)rder");
X   printcr(14,15,"s)et match");
X   printcr(14,16,"E)rase Files");
X   printcr(14,17,"D)elete");
X   printcr(14,18,"R)ename");
X   printcr(14,19,"?)HELP");
X   printcr(14,20,"Q)uit-not q");
X   printcr(32,16,"HDSCAN");
X   printcr(29,17,"Professional");
X   printcr(29,18,"Version 3.00");
X   }
X  standout();
X  if (ghost_flag==0) {
X   printcr(1,2,"                                            ");
X   printcr(1,4,"                                          ");
X   printcr(1,6,"                                          ");
X   printcr(1,7,"                                          ");
X   printcr(1,21,"                                                                             ");
X   printcr(1,10,"                                          ");
X   printcr(46,2,"  FILE NAMES (<=32 characters)   ");
X   printcr(16,3,"     Total Tagged          ");
X   printcr(1,5,"MODIFY        ACCESS        CHANGE        ");
X   printcr(1,6,"DATE:         DATE:         DATE:         ");
X   }
X  if (ghost_flag) {
X   printcr(1,5,"MODIFY");
X   printcr(1,6,"DATE:");
X   printcr(15,5,"ACCESS");
X   printcr(15,6,"DATE:");
X   printcr(29,5,"CHANGE");
X   printcr(29,6,"DATE:");
X   printcr(46,2,"FILE NAMES");
X   }
X  printcr(1,1,"PATH:");
X  printcr(1,3,"SIZE:");
X  printcr(21,3,"Tagged Size:");
X  if (xpert_flag) {
X    printcr(1,12,"MODE:");
X    printcr(1,13,"FNUM:");
X    printcr(1,14,"FNL: ");
X    printcr(1,15,"FUID:");
X    printcr(1,16,"FGID:");
X    }
X  standend();
X  cprompt();
X  }
X
Xprintcr(c,r,string)
Xint c,r;
Xchar *string;
X{
X  mvaddstr(r, c, string);
X  }
X
Xbprintcr(c,r,string)
Xint c,r;
Xchar *string;
X{
X  standout();
X  mvaddstr(r, c, string);
X  standend();
X  }
X
X/* scroll up or down */
Xout_it(dir)
Xint dir;
X{
X  pcnt=8;
X  if(pcnt>z) pcnt=z;
X  if(pcnt>=(selcnt-z)) pcnt=plen-(selcnt-z)+1;
X  Refresh();
X  }
X
Xdescend(path,level)
Xchar *path;
Xint level;
X{
X  register DIR *dirp;
X  register struct direct *d_entry;
X  register int yy;
X  long offset = 0L;
X  struct stat stb,sb;
X  int z;
X  if(got_int) return;
X  if(chdir(path) < 0) return;
X  dircnt++;
X  ondir=dircnt;
X  if(dircnt>=DIRMAX) {promptw("Sorry, too many dirs.[hit a key]"); bad_exit();}
X  strcpy(dirlev[level],path);
X  z=strlen(path);
X  dirlev[level][z+1]='\0';
X  strcpy(outbuf,dirlev[0]);
X  for(z=1;z<=level;z++) {
X    sprintf(tmpname,"%s/",&outbuf[0]);
X    sprintf(outbuf,"%s%s",&tmpname[0],&dirlev[z][0]);
X    }
X  sprintf(tmpname,"%s/",&outbuf[0]);
X  strcpy(dirpath[dircnt],tmpname);
X  sprintf(tmpname,"%s                                                                       ",tmpname);
X  sprintf(tmpname,"%.70s",tmpname);
X  printcr(6,1,tmpname);
X  refresh();
X  if((dirp = opendir(".")) == NULL) {
X    prompt("can't open \".\"");
X    ondir=lastdir[level];
X    if(level!=0) {
X      if(chdir("..") < 0) {    /* this should never happen...flws' */
X        promptw(" Nasty!. Can't change to \"..\" Bye!");
X        terminate();
X        }
X      }
X    return;
X    }
X  while(d_entry=readdir(dirp)) {
X    if (stat(d_entry->d_name, &stb) < 0) {
X      sprintf(tbtmp, "bad access: %s", &d_entry->d_name[0]);
X      prompt(tbtmp);
X      filesize[filecnt]=0;          /*  just dummy up everything */
X      file_cdate[filecnt]=0;
X      file_adate[filecnt]=0;
X      file_mdate[filecnt]=0;
X      filemode[filecnt]=0;
X      filefuid[filecnt]=0;
X      filefgid[filecnt]=0;
X      filefnum[filecnt]=0;
X      filefnl[filecnt]=0;
X      dirptr[filecnt]=ondir;
X      strcpy(filename[filecnt],d_entry->d_name);
X      filename[filecnt+1][32]='\0';
X      continue;
X      }
X    if(!strcmp(d_entry->d_name,".")|| !strcmp(d_entry->d_name,"..")) continue;
X    if(dirp->dd_fd >= MAXFILES-1) {
X      offset = telldir(dirp);
X      closedir(dirp);
X      dirp = NULL;
X      }
X    filesize[filecnt]=stb.st_size;
X    file_cdate[filecnt]=stb.st_ctime;
X    file_adate[filecnt]=stb.st_atime;
X    file_mdate[filecnt]=stb.st_mtime;
X    filemode[filecnt]=stb.st_mode;
X    filefuid[filecnt]=stb.st_uid;
X    filefgid[filecnt]=stb.st_gid;
X    filefnum[filecnt]=stb.st_ino;
X    filefnl[filecnt]=stb.st_nlink;
X    dirptr[filecnt]=ondir;
X    strcpy(filename[filecnt],d_entry->d_name);
X    filename[filecnt+1][32]='\0';
X    if(filecnt>=FILEMAX) {
X      promptw("Too many files! [hit a key]");
X      bad_exit();
X      }
X    if((stb.st_mode&S_IFMT) != S_IFDIR) Tagged[filecnt++]=' ';
X    else {
X      Tagged[filecnt++]='D';
X#ifndef NOTBSD
X      if (!trace_link) {                               /* not SysV */
X        if(lstat(d_entry->d_name,&sb) < 0) continue;
X        if((sb.st_mode&S_IFMT) == S_IFLNK) continue;
X        }
X#endif
X      lastdir[level+1]=ondir;
X      if(level<max_level) {
X        if(skip_cnt) {
X          for(yy=0;yy<skip_cnt;yy++)
X             if(!strcmp(d_entry->d_name,skip_path[yy])) goto skip_me;
X          }
X        descend(d_entry->d_name,level+1);
X        }
Xskip_me:
X      if(!dirp) {
X        if((dirp = opendir(".")) == NULL) {
X          promptw("can't reopen \".\"");
X          terminate();
X          }
X        seekdir(offset);
X        }
X      }
X    }
X  ondir=lastdir[level];
X  closedir(dirp);
X  if(level!=0) {
X    if(chdir("..") < 0) {
X      promptw(" can't change to \"..\"");
X      terminate();
X      }
X    }
X  return;
X  }
X
X#ifdef NOTBSD
Xint myfork()
X{
X  int pid,i;
X  static uint  del[]={1,1,2,2,4,4,8,8,16,16,32,64,128};
X  for(i=0;i<12&&(pid=fork())<0;++i) sleep(del[i]);
X  return(pid);
X  }
X
Xint mywait(pid)
Xint pid;
X{
X  int status;
X  while ((pid = wait(&status)) < 0) ;
X  if (status == 0) return (0);
X  return(-1);
X  }
X#endif
________This_Is_The_END________
if test `wc -l < hdscan.c` -ne 2569; then
	echo 'shar: hdscan.c was damaged during transit (should have been 2569 bytes)'
fi
fi		; : end of overwriting check
exit 0