#!/usr/bin/perl 
# $Id: mkfind,v 2.0 1996/03/13 15:45:45 rdwill Exp $
#
# $Log: mkfind,v $
# Revision 2.0  1996/03/13 15:45:45  rdwill
#  pushed revision to 2
#  revised parameter checker
#  restrict find to dirs, symlinks and regular files
#  checksum and mod-time generation's now much more straight forward...
#  checksum output for non-regular files and program error changed
#
# Revision 1.8  1994/07/08  01:55:30  rdwill
#  added switch '-dir' to allow generation of '00-find-ls' files for
#   directories without write permission!
#   (Is there any need for a '-file' switch?)
#  added switch '-csum' to calculate a checksum for each file with sum(1);
#   ATTENTION: there are various 'sum'-progs floating around, be carefull
#     to use the correct switches!
#   ATTENTION: this might prove to be VERY time/bandwidth consuming!!!
#
# Revision 1.7  1994/01/07  00:44:06  rdwill
#  fixed the 'date' bug, which showed up after 01.01.94...
#
# Revision 1.6  1993/07/29  22:51:58  rdwill
#  extensively rearranged command line switches and their handling;
#   obsoleted switches keep their semantics!
#  reversed order of date and size for readability.
#  fixed a feature of epix (using '/usr/bin/date' now).
#  improved output of time-stamps by examining the last-modification-
#   date directly, no longer pretending all files, which are older than
#   6 months have been last modified at midnight (as 'ls -l' will only
#   show us the year, NO time).
#  cheated with log message of revision 1.4.
#
# Revision 1.5  1993/06/29  00:55:36  rdwill
#  added error handling to gzip extension checking.
#  added switch '-other' to output ONLY permissions for "others" (as opposed
#   to user and group) freeing up 6 characters per line. the omitted
#   permissions could be useful to only a very small number of users anyway.
#
# Revision 1.4  1993/06/28  23:41:16  rdwill
#  added automatic gzip-extension checking.
#  introduced 'sortable-date', a new output format for time-stamps,
#   which is human readable and also easily sorted by time
#   (ascending/[descending: newest first]) via 'sort [-r] +1 -2 +3'.
#
# Revision 1.3  1993/06/18  01:14:32  rdwill
#  gzip(1) replaces compress(1) as default compressor.
#  capitalized most variable names.
#  improved location of overwrite warning.
#  eliminated self-references.
#  restricted output to minimum.
#  improved verbose message.
#  more (IMHO) pretty pinting.
#
# Revision 1.2  1993/06/18  01:02:22  rdwill
#  added 'help'-switch and message.
#  added switch to choose between gzip(1) and compress(1).
#  output is now sorted by name.
#  replaced explicit commands with variables and removed absolute paths.
#  pretty pinting (IMHO).
#  modified error messages.
#
#

chop( $C = `basename $0`);

sub Usage {
  print <<EODQ, <<'EOSQ';

NAME
    $C - create a sort of listing

SYNOPSIS
    $C [options]

DESCRIPTION
    Create a comprehensive, recursive, sorted listing of all files and
    directories under the current one, both in compressed and uncompressed
    form.

OPTIONS
    --help           give this message and exit.
    --quiet          don't ask silly questions, just do it.
    --verbose        opposite of -quiet. [DEFAULT]
    --gzip           use GNU gzip(1) for maximum compression. [DEFAULT]
    --compress       use compress(1) for conservative compression.
    --no-perms       output no permissions at all. (only regular files!)
    --others-only    output only permissions for others. [DEFAULT]
    --full-perms     output full permissions.
    --ls-date        use ls-style date.
    --sort-date      use sortable date. [DEFAULT]
    --time           dig out modification hour and minute. [DEFAULT]
    --no-time        date only, no time. (only effective with '--sort-date')
    --csum           create a checksum (cksum(1)) in the 4th column.
    --dir directory  result file will be written to 'directory'.

VERSION
EODQ
    $Id: mkfind,v 2.0 1996/03/13 15:45:45 rdwill Exp $

EOSQ

  exit( 1);
}

# Preparations
  select( STDERR);
  $| = 1;
  select( STDOUT);
  $[ = 0;

# Set DEFAULT
  $Verbose = 1;
  $UseGzip = 1;
  $PrintPerms = 1;
  $SortableDate = 1;
  $PrintTime = 1;
  $CSum = 0;
  $Dir = ".";

sub PrintOptions {
  print( "Help=$Help\n");
  print( "Verbose=$Verbose\n");
  print( "UseGzip=$UseGzip\n");
  print( "PrintPerms=$PrintPerms\n");
  print( "SortableDate=$SortableDate\n");
  print( "PrintTime=$PrintTime\n");
  print( "CSum=$CSum\n");
  print( "Dir=$Dir\n");
}

# The following (simple) parameter checker modifies variables 
# without check for duplications, contradictions etc.
# If contradictory parameters are used the order of their appearance
# on the command line is sigificant. That parameter which occurs
# last wins. The inoffical one-letter-abbreviations can't cumulate,
# i.e. '-hv'  will be ignored. Apropos ignored -- there is no such
# thing as an 'unrecognized option', illegal options will be gracefully
# skipped ...
while ($_ = $ARGV[0], /^-/) {
  shift;
  last if /^--$/;
  (/^--?help/ || /^-h/)			&& ($Help = 1);

# --quiet <==> --verbose
  (/^--?quiet/ || /^-q/)		&& ($Verbose = 0);
  (/^--?verbose/ || /^-v/)		&& ($Verbose = 1);

# --compress <==> --gzip
  /^--?compress/			&& ($UseGzip = 0);
  /^--?gzip/				&& ($UseGzip = 1);

# --no-perms <==> --full-perms <==> --others-only
  (/^--?no-perms/ || /^--?nop/)		&& ($PrintPerms = 0);
  (/^--?others-only/ || /^--?others/)	&& ($PrintPerms = 1);
  (/^--?full-perms/ || /^--?full/)	&& ($PrintPerms = 2);

# --ls-date <==> --sort-date
  /^--?ls-date/				&& ($SortableDate = 0);
  /^--?sort-date/			&& ($SortableDate = 1);

# --no-time <==> --exact-time
  /^--?no-time/				&& ($PrintTime = 0);
  (/^--?time/ || /^-t/)			&& ($PrintTime = 1);

  (/^--?csum/ || /^-c$/)		&& ($CSum = 1);

  (/^--?dir/ || /^-d$/)			&& ($Dir = $ARGV[0], shift);
}


if ( $Help ) {
  &Usage;
}

if ( $Dir ) {
  if ( ! -d $Dir ) {
    die ( "$C: ERROR: no directory: '$Dir'\n");
  }
}

# compression setup
if ( $UseGzip ) {
  $Gzip = 'gzip';
  $GzipOpt = '-9f';

  # check suffix
  $Tmp = "/tmp/$C-$$";
  system( "cp $0 $Tmp") &&
	die ( "$C: ERROR: cp (0): $!\n");
  $ENV{GZIP} = $GzipOpt;
  system( "$Gzip $Tmp") &&
	die ( "$C: ERROR: $Gzip: $!\n leftover in '$Tmp'.\n");
  if ( -r "$Tmp.gz" ) {
    $Gz = '.gz';
  } else {
    $Gz = '.z';
  }
  unlink( "$Tmp$Gz") ||
	die( "$C: ERROR: unlink $Tmp$Gz: $!\n leftover in '$Tmp$Gz'.\n");

} else {
  $Gzip = 'compress';
  $GzipOpt = '-f';
  $Gz = '.Z';
}

# utility setup
$CpPreserve = 'cp -p';
$Find = 'find';
$Sort = 'sort';
$Date = '/bin/date';
$Sum = 'cksum';

# output variables
$Tmp = "/tmp/$C-$$";
$Res = "$Dir/00-find-ls";

if ( $Verbose ) {
  print <<EOF;

$C will create two files in the current working directory. The
contents of which is roughly described by the following commands:

  % $Find . -ls | some-perl-code-to-remove-unwanted-information > $Res 
  % $Gzip -c $Res > $Res$Gz

Note:
  For further information use '$C -help'. If '$C' terminates
  ABNORMALLY, there might be a leftover in '$Tmp'!

EOF

  printf( "OK to do so? (Y/n) ");
  $Answer = <STDIN>;
  if ( $Answer =~ m:[nN].*: ) {
    $! = -1;
    die(" User abort.\n");
  } 

  if ( -f "$Res" || -f "$Res$Gz" ) {
    print( "Ok to overwrite old '$Res'? (Y/n) ");
    $Answer = <STDIN>;
    if ( $Answer =~ m:[nN].*: ) {
      $! = -1;
      die(" User abort.\n");
    } 
  }
}

#open( FIND, "$Find . -ls | $Sort +10|") ||
open( FIND, "$Find . \\( -type f -o -type d -o -type l \\) -ls | $Sort +10|") ||
	die( "$C: ERROR: open find-pipe: $!\n");

open( RESULT, "> $Tmp") ||
	die( "$C: ERROR: open scratch-file: $!\n");
select( RESULT);

#$| = 1;

<FIND>;
while ( <FIND> ) {
  chop;
  s/^\s*//;
  ($Ino, $Blk, $Acc, $Lnk, $Uid, $Gid, $Siz, $Mo, $Da, $T_Y, $N, @Rst) = split;
  $N =~ s:^\.\/::;
  if ( "$Dir" eq "." && ("./$N" eq "$Res" || "./$N" eq "$Res$Gz") ) {
    next;
  }

  if ( $#Rst >= 0 ) {
    unshift( @Rst, "");
#   print( STDERR "(#Rst == $#Rst) >= 0 : {@Rst}\n");
# } else {
#   print( STDERR "No Rst ($#Rst) ? {$_}\n");
  }

  $Acc = "$Acc ";
  if ( $PrintPerms == 1 ) {
    $Acc =~ s/^(.)......(...)/$1$2/;
  } elsif ( $PrintPerms == 0 ) {
    if ( $Acc =~ m:^([^-]).........: ) {
      print( STDERR "Not a file: [$1] $N\n");
      next;
    } else {
      $Acc = "";
    }
  }

  if ( $CSum ) {
    if ( -f $N ) {
      $FSum = `$Sum $N`;
      # WARNING: the next action depends on cksum...
      if ( $FSum =~ m:^([\s\d]{9}\d{1})\s: ) {
        $CSumF = "$1";
      } else {
        $CSumF = "        08";
      }
    } else {
      $CSumF = "        00";
    }
    $Siz = sprintf( "%7s %10s", $Siz, $CSumF); 
  }
  if ( $SortableDate ) {
    local( $mtime ) = (lstat($N))[9];
    local( $sec, $min, $hour, $mday, $mon, $year) = localtime( $mtime);
    $Date = sprintf("%02d/%02d/%02d", $year, $mon + 1, $mday);
    if ( $PrintTime ) {
      $Time = sprintf("%02d:%02d", $hour, $min);
      printf("%s%s-%s %7s %s@Rst\n", $Acc, $Date, $Time, $Siz, $N);
    } else {
      printf("%s%s %7s %s@Rst\n", $Acc, $Date, $Siz, $N);
    }
  } else {	# --ls-date
    printf("%s%9s  %s %2s %5s %s@Rst\n", $Acc, $Siz, $Mo, $Da, $T_Y, $N);
  }
}
 
close( FIND);
close( RESULT);

select( STDERR);
$| = 1;


system( "$CpPreserve $Tmp $Res\n") &&
	die ( "$C: ERROR: cp (1): $!\n");

system( "$Gzip $GzipOpt $Res\n") &&
	die ( "$C: ERROR: $Gzip: $!\n leftover in '$Tmp'.\n");

system( "$CpPreserve $Tmp $Res\n") &&
	die ( "$C: ERROR: cp (2): $!\n leftover in '$Tmp'.\n");

unlink( "$Tmp") ||
	die( "$C: ERROR: unlink $Tmp: $!\n leftover in '$Tmp'.\n");

