Article 12017 of comp.lang.perl: Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.utdallas.edu!wupost!howland.reston.ans.net!EU.net!sun4nl!ruuinf!henkp From: henkp@cs.ruu.nl (Henk Penning) Subject: Re: 'tail -f' & PERL Message-ID: Keywords: syslog, tail Sender: usenet@cs.ruu.nl (Six O'Clock News) Organization: Utrecht University, Dept. of Computer Science References: <2mn0d7$jod@decuk.uvo.dec.com> Date: Sat, 26 Mar 1994 16:00:30 GMT Lines: 806 In <2mn0d7$jod@decuk.uvo.dec.com> stuart@new.dec.com (Stuart Broderick) writes: >I want to carry out monitoring of multiple files [...] Below is a script+manpage which is basicly 'xtail in perl'. It watches files (and directories) and reports changes. An extra feature is the possibility to 'remember' the state of a set of files, so you can inspect the changes that occurred since the last time you looked. Hope this helps === HenkP === Henk P. Penning, Dept of Computer Science, Utrecht University \__/ \__/ \ Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands. \__/ \__/ Telephone: +31-30-534106, fax: 513791, NIC-handle: HPP1 \__/ \__/ \__/ \ e-mail : henkp@cs.ruu.nl (uucp to sun4nl!ruuinf!henkp) _/ \__/ \__/ \__/ #!/bin/sh # This is a shell archive (shar 3.32) # made 03/26/1994 15:49 UTC by henkp@cs.ruu.nl # Source directory /amd/alchemy/home/henkp/src/henkp/ptail # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 16275 -rwxr-x--- ptail # 4845 -rwx------ ptail.1 # if touch 2>&1 | fgrep 'amc' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= ptail ============== echo "x - extracting ptail (Text)" sed 's/^X//' << 'SHAR_EOF' > ptail && X#!/net/bin/perl X X$SIG_QUIT = 'QUIT' ; X$SIG_INTR = 'INT' ; X$MAGIC = 'ptail magic number 22' ; X X$opt_max = 60 ; X X@STAT = ($DEV,$INO,$MODE,$NLINK,$UID,$GID,$RDEV,$SIZE,$ATIME,$MTIME,$CTIME, X $BLKSIZE,$BLOCKS) = (0..12) ; X@TIME = ($SEC,$MIN,$HOUR,$MDAY,$MON,$YEAR,$WDAY,$YDAY,$ISDST) = (0..8) ; X X$globpat = '[*?[]' ; X$__ = '------' ; X$_x = '******' ; X$homedir = &homedir ; X$replen = 75 ; X X$prog = substr($0,rindex($0,'/')+1) ; X$Usage = <$opt_log") || &Error("Can't write LOG $opt_log ($!)") ; X local($old) = select LOG ; $| = 1 ; X select($old) ; X } X Xsub log { print @_ ; print(LOG @_) if $opt_log ; $modified = 1 ; } X X&Error("Option 'max' must not be negative ($opt_max)") if $opt_max < 0 ; X X$RC = &name_rc($opt_f) if defined $opt_f ; X X$SIG{$SIG_QUIT} = 'handle_QUIT' ; X$SIG{$SIG_INTR} = 'handle_INTR' ; X Xsub cmp_dirs # return x if x in @old && x not_in @new X { local($_,%new) ; X grep($new{$_}++,splice(@_,0,shift)) ; X return sort grep(!$new{$_},@_) ; X } X Xsub stat X { local($obj,@res) = @_ ; X if ( $obj =~ /$globpat/o ) X { return 'DIR', <${obj}> ; } X elsif ( -d $obj ) X { opendir(DIR,$obj) || return('UNK') ; X @res = readdir(DIR) ; X closedir(DIR) ; X splice(@res,0,2) ; X return 'DIR', sort grep(s|^|$obj/|,@res) ; X } X elsif ( -f _ ) X { return 'FIL', stat(_) ; } X else X { return 'UNK' ; } X } X Xsub inspect_obj X { local($obj) = @_ ; X local($same,$changed) = 1 ; X ($res,@res) = &stat($obj) ; X if ( $type{$obj} ne $res ) X { # TYPE CHANGE X # DELETE THE OLD STUFF X X $same = 0 ; X X if ( $type{$obj} eq 'DIR' ) X { &rep_stat("$__ DIR $obj gone\n") ; X for ( split($;,$stat{$obj}) ) X { delete $fil{$_} ; } X } X elsif ( $type{$obj} eq 'FIL' ) X { &rep_stat("$__ FIL $obj gone\n") ; } X elsif ( $type{$obj} ne 'UNK' ) X { &Error("SYSERR: del <$obj> type <$type{$obj}>") ; } X X # INIT THE NEW STUFF AS ON START-UP X X $type{$obj} = $res ; X if ( $res eq 'DIR' ) X { @res = grep((-f $_ ? $fil{$_} = join($;,stat(_)) : 0),@res) ; X &rep_stat("$__ type change: $obj is now a directory\n") ; X } X elsif ( $res eq 'FIL' ) X { &rep_stat("$__ type change: $obj is now a file\n") ; X &rep_file("FIL",$obj,$res[$SIZE]) ; X } X elsif ( $res ne 'UNK' ) X { &Error("SYSERR: type change <$obj> to <$res>") ; } X $stat{$obj} = join($;,@res) ; X } X else X { # DETERMINE if $obj changed state, and report X X @stat = split($;,$stat{$obj}) ; X if ( $res eq 'FIL' ) X { if ( $res[$SIZE] != $stat[$SIZE] ) X { &rep_file("FIL",$obj,$res[$SIZE],$stat[$SIZE]) ; X $same = 0 ; X } X $stat{$obj} = join($;,@res) ; X } X elsif ( $res eq 'DIR' ) X { @del = &cmp_dirs(scalar(@res),@res,@stat) ; X print "DIR $obj:\nres @res\nstat @stat\ndel @del\n" if $opt_d ; X $same = 0 if @del ; X for ( @del ) X { # next unless defined $fil{$_} ; X &rep_stat("$__ fil $_: gone\n") ; X delete $fil{$_} ; X } X for ( @res ) X { $changed = 1 ; X @nstat = stat($_) ; X unless ( @nstat ) X { &rep_stat("$_x fil $_: can't stat, assume gone\n") ; X delete $fil{$_} ; X } X elsif ( ! -f _ && defined $fil{$_} ) X { # was a file, but not now X &rep_stat("$__ fil $_: not a file anymore\n") ; X delete $fil{$_} ; X } X elsif ( ! -f _ ) X { # not a file, ignore it X &rep_stat("$__ fil $_: not a file, ignore\n") if $opt_d ; X $changed = 0 ; X } X elsif ( defined($fil{$_}) ) X { @ostat = split($;,$fil{$_}) ; X if ( $nstat[$SIZE] != $ostat[$SIZE] ) X { &rep_file("fil",$_,$nstat[$SIZE],$ostat[$SIZE]) ; } X else X { $changed = 0 ; } X $fil{$_} = join($;,@nstat) ; X } X else X { &rep_file("fil",$_,$nstat[$SIZE]) ; X $fil{$_} = join($;,@nstat) ; X } X $same &= ! $changed ; X } X $stat{$obj} = join($;,grep(defined($fil{$_}),@res)) ; X } X elsif ( $res ne 'UNK' ) X { &Error("SYSERR: obj <$obj> type <$res>") ; } X } X return $same ; X } X Xsub inspect_objs X { for $obj ( @obj ) X { if ( $time{$obj} > 0 ) X { # clock still ticking, assume unchanged X $time{$obj}-- ; X } X else X { if ( &inspect_obj($obj) ) X { $rate{$obj}++ if $rate{$obj} < $opt_max ; } X else X { $rate{$obj} = 0 ; } X $time{$obj} = $rate{$obj} ; ; X } X } X } X Xsub init_obj X { local($obj) = @_ ; X ($res,@res) = &stat($obj) ; X $type{$obj} = $res ; X if ( $res eq 'DIR' ) X { @res = grep( ( -f $_ ? $fil{$_} = join($;,stat(_)) : 0 ), @res) ; } X elsif ( $res ne 'FIL' && $res ne 'UNK' ) X { &Error("SYSERR: init <$obj> type <$res>") ; } X $stat{$obj} = join($;,@res) ; X } X Xsub reset_rate_time X { for ( @obj ) X { $rate{$_} = 0 ; X $time{$_} = 0 ; X } X } X Xsub init X { &read_rc($RC) if length($RC) ; X &set_state(keys %type) if $opt_init ; X for $ARG ( @ARGV ) X { unless ( defined $type{$obj} ) X { &init_obj($ARG) ; } X } X @obj = sort keys %type ; X &Error("Nothing to watch !?") unless @obj ; X &reset_rate_time ; X } X Xsub doit X { if ( $interp ) X { &interp ; } X else X { &inspect_objs ; } X } X Xsub dash X { local($x) = @_ ; X local($nl) ; X $nl = chop($x) if substr($x,-1,1) eq "\n" ; X if ( length($x) < $replen ) X { $x .= ' ' ; X $x .= '-' x ($replen - length($x)) ; X } X return $x . $nl ; X } X Xsub rep_file X { local($pref,$file,$nsize,$osize) = @_ ; X local($incr,$chunk) = ($nsize-$osize,10240) ; X local($tmp,$from,$FH,$size,$rep) ; X if ( @_ == 3 ) # implying : not seen before X { $rep = sprintf("$__ $pref $file: size %d (new)\n", $nsize) ; X $from = 0 ; X $size = $nsize ; X $rep_file = undef ; X } X elsif ( @_ == 4 && $incr == 0 ) X { &Error("SYSERR: incr0 <$file>") ; } X elsif ( $incr > 0 ) X { $rep = sprintf("$__ $pref $file: size %d (%+d)\n", $nsize, $incr) ; X $from = $osize ; X $size = $incr ; X } X else # file shrunk ( $incr < 0 ) X { $rep = sprintf("$__ $pref $file: size %d (%+d)\n", $nsize, $incr) ; X $from = 0 ; X $size = $nsize ; X $rep_file = undef ; X } X X &log(&dash($rep)) if $opt_v || $file ne $rep_file ; X $rep_file = $file ; X X return unless $FH = &open($file) ; X X if ( $from > 0 ) X { unless ( seek($FH,$from,0) ) X { &log("$_x Can't seek $file to $from from 0\n") ; X &close($FH) ; X return ; X } X } X X while ( $size > 0 ) X { $chunk = ( $size < $chunk ) ? $size : $chunk ; X sysread($FH,$tmp,$chunk) ; X &log($tmp) ; X $size -= $chunk ; X } X &close($FH) ; X } X Xsub rep_stat X { local($rep) = @_ ; X &log(&dash($rep)) ; X $rep_file = undef ; X } X Xsub open { return( open(NEW,$_[0]) ? 'NEW' : undef ) ; } Xsub close { return( close(NEW) ? 'NEW' : undef ) ; } X Xsub handle_QUIT { exit ; } Xsub handle_INTR X { $SIG{$SIG_INTR} = 'handle_INTR' ; X $interp = 1 ; X } X Xsub report X { local($type) = @_ ; X local($obj,$_) ; X $| = 1 ; X print "============ REPORT ============\n" ; X for $obj ( @obj ) X { print "time $time{$obj}, rate $rate{$obj}, max $opt_max\n" ; X if ( $type{$obj} eq 'FIL' ) X { printf "FIL %s %s\n", &mtime($stat{$obj}), $obj ; } X elsif ( $type{$obj} eq 'DIR' ) X { print "DIR $obj\n" ; X if ( $type eq 'full' ) X { for ( split($;,$stat{$obj}) ) X { printf " %s %s\n", &mtime($fil{$_}), $_ ; } X } X } X elsif ( $type{$obj} eq 'UNK' ) X { print "UNK $obj\n" ; } X else X { print "error: type $obj = <$type{$obj}>\n" ; } X } X print "default dump file: $RC\n" if length($RC) ; X printf "watching: DIRs %d, files %d, unknowns %d\n", X scalar(grep($_ eq 'DIR', values %type)), X scalar(grep(1, keys %fil)) + scalar(grep($_ eq 'FIL',values %type)), X scalar(grep($_ eq 'UNK', values %type)) ; X print "========== REPORT END ==========\n" ; X } X Xsub mtime X { &print_time X ( "%04d-%02d-%02d %02d:%02d:%02d", # format X localtime((split($;,$_[0]))[$MTIME]), # time X $YEAR,$MON,$MDAY,$HOUR,$MIN,$SEC # fields X ) ; X } X Xsub print_time X { local($format) = shift ; X local(@time) = splice(@_,0,scalar(@TIME)) ; X $time[$MON]++ ; X $time[$YEAR] += 1900 ; X sprintf($format,@time[@_]) ; X } X Xsub init_HELP_MENU { X$HELP = < " ; X $full_ans = $ans = ; X unless ( length($ans) ) X { print "no input\n" ; next ; } X chop $ans ; X $ans = length($ans) ? substr($ans,0,1) : $def ; X if ( $ans =~ /^[hH?]$/ ) X { print $HELP ; } X elsif ( $ans eq 'c' ) X { $spent = time - $entered ; X for $obj ( @obj ) X { $time{$obj} -= $spent ; } X last ; X } X elsif ( $ans eq 'q' ) X { &exit ; } X elsif ( $ans eq 'x' ) X { exit ; } X elsif ( $ans eq 'r' ) X { &report() ; X $def = $ans ; X } X elsif ( $ans eq 'R' ) X { &report('full') ; X $def = $ans ; X } X elsif ( $ans eq 'i' ) X { $same = 1 ; X for $obj ( @obj ) X { $same &= &inspect_obj($obj) ; } X print "inspect: no change\n" if $same ; X $def = $ans ; X } X elsif ( $ans eq 's' ) X { printf "save on [%s]: ", length($RC) ? $RC : 'no default' ; X $ans = ; X unless ( length($ans) ) X { print "no input: not saved\n" ; next ; } X chop $ans ; X $ans = $RC unless length($ans) ; X unless ( length($ans) ) X { print "empty answer and no default: not saved\n" ; } X else X { $ans = &name_rc($ans) ; X if ( &dump_rc($ans) ) X { $rcdef = $RC = $ans ; X &init_HELP_MENU ; X } X } X } X elsif ( $ans eq 'b' ) X { kill('TSTP',0) ; X $def = 'i' ; X } X elsif ( $ans eq '!' ) X { $full_ans = substr($full_ans,1) ; X system($full_ans) ; X } X else X { print "not on the menu ($ans)\n" ; } X } X $interp = 0 ; X } X Xsub main X { &init ; X if ( $opt_list ) { &report('full') ; exit ; } X if ( $opt_once ) { &doit ; &exit ; } X while ( 1 ) X { sleep 1 ; X &doit ; X } X } X X&main ; X Xsub homedir X { local($home) = $ENV{'HOME'} || (getpwuid($<))[7] ; X &Error("can't get homedir") unless $home ; X return $home ; X } X Xsub name_rc X { local($dump) = @_ ; X local($rc) ; X $dump =~ s/^\s+// ; X $dump =~ s/\s+$// ; X return '' unless length($dump) ; X unless ( $dump =~ /^=/ ) X { $rc = $dump ; } X else X { $dump = substr($dump,1) ; X $rc = $homedir . "/.rc" ; X -d $rc || mkdir($rc,0777 & ~umask) || &Error("can't mkdir $rc ($!)") ; X $rc .= "/$prog" ; X -d $rc || mkdir($rc,0777 & ~umask) || &Error("can't mkdir $rc ($!)") ; X $rc .= "/$dump" ; X } X if ( -e $dump && ! -f $dump ) X { &Warn("not a file: $rc") ; X $rc = '' ; X } X return $rc ; X } X Xsub exit X { if ( length($RC) ) X { &dump_rc($RC) ; } X exit ; X } X Xsub put_stat X { local($stat,@stat) = @_ ; X @stat = split($;,$stat) ; X $stat[$MTIME] = sprintf("%+d", $stat[$MTIME]-$stat[$CTIME]) ; X $stat[$ATIME] = sprintf("%+d", $stat[$ATIME]-$stat[$CTIME]) ; X return join(' ',@stat) ; X } X Xsub get_stat X { local($stat,@stat) = @_ ; X @stat = split(' ',$stat) ; X $stat[$MTIME] += $stat[$CTIME] ; X $stat[$ATIME] += $stat[$CTIME] ; X return join($;,@stat) ; X } X Xsub dump_rc X { local($RC) = @_ ; X local($obj,$fil,@stat) ; X unless ( $modified ) X { &Warn("no change, dumping anyway") ; X } X unless ( open(RC,">$RC") ) X { &Warn("dump failed, can't write to $RC ($!)") ; X return 0 ; X } X else X { print RC "$MAGIC\n# Don't delete the first line\n" ; X for $obj ( @obj ) X { printf RC "%s %s\n", $type{$obj}, $obj ; X if ( $type{$obj} eq 'DIR' ) X { for $fil ( split($;,$stat{$obj}) ) X { printf RC "fil %s\n", $fil ; X printf RC "stt %s\n", &put_stat($fil{$fil}) ; X } X } X elsif ( $type{$obj} eq 'FIL' ) X { printf RC "STT %s\n", &put_stat($stat{$obj}) ; } X elsif ( $type{$obj} ne 'UNK' ) X { &Error("SYSERR: obj <$obj> has type <$type{$obj}>") ; } X } X close(RC) || &Warn("can't close $RC") ; X print "status dumped in $RC\n" ; X $modified = 0 ; X return 1 ; X } X } X Xsub read_rc X { local($RC) = @_ ; X local($what,$tail,@tail,$obj,$fil) ; X open(RC) || return ; X $what = ; chop $what ; X &Error("Error: first line in $RC should be:\n$MAGIC\nbut is\n$what") X if $what ne $MAGIC ; X while ( ) X { chop ; X next if /^#/ ; X $what = substr($_,0,3) ; X $tail = substr($_,4) ; X if ( $what eq 'DIR' ) X { $obj = $tail ; X $type{$obj} = 'DIR' ; X } X elsif ( $what eq 'fil' ) X { $stat{$obj} .= "$tail$;" ; X $fil = $tail ; X } X elsif ( $what eq 'stt' ) X { $fil{$fil} = &get_stat($tail) ; } X elsif ( $what eq 'FIL' ) X { $obj = $tail ; X $type{$obj} = 'FIL' ; X } X elsif ( $what eq 'STT' ) X { $stat{$obj} = &get_stat($tail) ; } X elsif ( $what eq 'UNK' ) X { $type{$tail} = 'UNK' ; X $stat{$tail} = undef ; X } X else X { &Error("shouldn't occur in -f argument: $_" ) ; } X } X for ( keys %type ) { chop $stat{$_} if $type{$_} eq 'DIR' ; } X close(RC) || &Error("can't close $RC") ; X } X Xsub set_state X { %type = %stat = %fil = () ; X for ( @_ ) { &init_obj($_) ; } X } SHAR_EOF $TOUCH -am 0326164894 ptail && chmod 0750 ptail || echo "restore of ptail failed" set `wc -c ptail`;Wc_c=$1 if test "$Wc_c" != "16275"; then echo original size 16275, current size $Wc_c fi # ============= ptail.1 ============== echo "x - extracting ptail.1 (Text)" sed 's/^X//' << 'SHAR_EOF' > ptail.1 && X.TH PTAIL 1L "RUU-CS" X.SH NAME Xptail \- watching the files grow X.SH SYNOPSIS X.B ptail X[\fB-v\fP] X[\fB-max\fP \fItime\fP] X[\fB-log\fP \fIlog\fP] X[\fB-f\fP \fIfile\fP] X.I "[obj ...]" X.br X.B ptail X\fB-once\fP X\fB-f\fP \fIfile\fP X.I "[obj ...]" X.br X.B ptail X\fB-init\fP X\fB-f\fP \fIfile\fP X.I "[obj ...]" X X.SH DESCRIPTION XProgram \fBptail\fP watches the filesystem objects specified in Xthe argument list. XAt any given moment, \fIobj\fP is either a plain file, Xa directory (or globbing pattern if it contains any of '*?['), Xor unknown. X XIf \fIobj\fP is a plain file, size changes are reported. XIf the file is readable, data added to the file is shown. XIf the file shrinks, the entire contents is shown. X.br XIf \fIobj\fP is a directory (or globbing pattern), X\fIptail\fP watches all the plain files in the directory (or all the Xplain files which are found when the globbing pattern is expanded). X XIf the type of an object changes, its old state is forgotten, and it Xis treated as a new object of the new type. XIf, for instance, an object changes from a plain file into a directory, Xall the plain files in the directory are shown Xand watched for further changes. X XInitially, every object is inspected every second. XBut if \fIptail\fP finds it hasn't changed, X\fIptail\fP ignores the object for 2 seconds. XIf the object hasn't changed after 2 seconds, X\fIptail\fP ignores the object for 3 seconds, etc. X.br XIn general, \fIptail\fP keeps a timeout for every object. XIf the timeout expires, \fIptail\fP inspects the object. XIf the object has changed, \fIptail\fP reports Xand resets the timeout to 1. XIf the object hasn't changed, X\fIptail\fP increments the timeout for the object Xunless some upperbound (usually 60 seconds) is reached. XSo every object is inspected at least every 60 seconds X(use option \fB-max\fP to change this default 60). X.SH OPTIONS X.PP XProgram \fBptail\fP recognizes the following options: X.IP "\fB-v\fP" XBe verbose, report stat info on every change. X.IP "\fB-max\fP \fImax\fP" XSet maximum time between inspections to \fImax\fP seconds. X.IP "\fB-log\fP \fIlog\fP" XLog all changes in file \fIlog\fP. X.IP "\fB-f\fP \fIfile\fP" XProgram \fBptail\fP keeps status info on all the objects it watches. XIf the \fB-f\fP-option is specified, \fBptail\fP dumps this info Xin file \fIfile\fP on normal exit. XProgram \fBptail\fP can read such files on startup, report the changes Xthat have occurred since \fIfile\fP was dumped, Xand resume watching the objects. X XIf a \fIfile\fP starts with '=' (as is '=\fIname\fP') it is interpreted Xas '\fIhomedir\fP/.rc/ptail/\fIname\fP', where \fIhomedir\fP is Xthe value of environment variable HOME or, if HOME is not defined, Xthe home-directory of the current real uid, Xas reported by \fBgetpwuid\fP. X.IP "\fB-once\fP" XInspect and report only once. XUseful only together with the \fB-f\fP-option. X.IP "\fB-init\fP" XIgnore previous state of objects found in state dumps. XTake current state of objects as initial state. XUseful only together with the \fB-f\fP-option. X.PP X.SH SIGNALS XOn signal QUIT (usually the result of typing Control-\\), \fBptail\fP Xexits without further ado. X XOn signal INTR (usually the result of typing Control-C), \fBptail\fP Xsuspends its watching activities and enters command-mode. XIn command mode, a menu is presented and the user may issue commands. XOnly the first char of the command is significant. X.IP "?" XShow info on all commands and currents defaults. X.IP continue XLeave command mode and resume watching objects, X.IP quit XQuit \fBptail\fP, dump status if requested. X.IP xit XQuit \fBptail\fP immediately. X.IP inspect XInspect all objects, report changes if any. X.IP report XShow some status of all objects being watched. X.IP Report XShow some status of all objects being watched. XAlso show status of files in directories and globs. X.IP save XSave current status. The user is prompted for for a filename, Xcurrent default given. XThe default changes to the filename given if the dump was succesful. XIf a filename like '=\fIname\fP' is given it is interpreted Xas '\fIhomedir\fP/.rc/ptail/\fIname\fP', where \fIhomedir\fP is Xthe value of environment variable HOME or, if HOME is not defined, Xthe home-directory of the current real uid, Xas reported by \fBgetpwuid\fP. X.IP background XSuspend X.BR ptail . XUsualy the same as hitting Cntl-Z. X.PP X.SH "SEE ALSO" Xperl(1), xtail(1), getpwuid(3C) X.SH AUTHOR XHenk P. Penning (Email: henkp@cs.ruu.nl) X.br XUtrecht University, Dept. Comp. Sci., the Netherlands. X.SH ACKNOWLEDGEMENT XChip Rosenthal provided us with \fBxtail\fP, Xwhich was a great improvement over 'tail -f'. XCompared to \fBxtail\fP, X\fBptail\fP adds only a little functionality, Xcould be considered more portable, Xobserves a few more changes, Xand is undoubtedly more expensive. X.SH BUGS XPlease report to the author. X.SH FILES X$HOME/.rc/ptail Default directory for storing stat-files. X SHAR_EOF $TOUCH -am 0326164894 ptail.1 && chmod 0700 ptail.1 || echo "restore of ptail.1 failed" set `wc -c ptail.1`;Wc_c=$1 if test "$Wc_c" != "4845"; then echo original size 4845, current size $Wc_c fi exit 0 -- Henk P. Penning, Dept of Computer Science, Utrecht University \__/ \__/ \ Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands. \__/ \__/ Telephone: +31-30-534106, fax: 513791, NIC-handle: HPP1 \__/ \__/ \__/ \ e-mail : henkp@cs.ruu.nl (uucp to sun4nl!ruuinf!henkp) _/ \__/ \__/ \__/