home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume10 / dusage.pl < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  18.6 KB

  1. From decwrl!lll-winken!uunet!allbery Sun Feb 18 00:50:13 PST 1990
  2. Article 1336 of comp.sources.misc:
  3. Path: decwrl!lll-winken!uunet!allbery
  4. From: jv@mh.nl (Johan Vromans)
  5. Newsgroups: comp.sources.misc
  6. Subject: v10i065: Watching disk usage
  7. Message-ID: <78924@uunet.UU.NET>
  8. Date: 14 Feb 90 02:03:05 GMT
  9. Sender: allbery@uunet.UU.NET
  10. Lines: 676
  11. Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  12.  
  13. Posting-number: Volume 10, Issue 65
  14. Submitted-by: jv@mh.nl (Johan Vromans)
  15. Archive-name: dusage.pl
  16.  
  17. Guarding disk space is one of the problems of system management. 
  18.  
  19. Some time ago I converted an old awk/sed/sh script to keep track of
  20. disk usage to a new perl program, added new features, options; even
  21. wrote a manual page.
  22.  
  23. Using a list of pathnames, this program filters the output of du(1) to
  24. find the amount of disk space used for each of the paths (actually, it
  25. collects all values in one single du run). It adds the new value to
  26. the list, shifting old values up. It then generates a nice report of
  27. the amount of disk space occupied in each of the specified paths,
  28. together with the amount it grew (or shrinked) since the previous run,
  29. and since 7 runs ago. When run daily, this gives daily and weekly
  30. figures.
  31.  
  32. #---------------------------------- cut here ----------------------------------
  33. # This is a shell archive.  Remove anything before this line,
  34. # then unpack it by saving it in a file and typing "sh file".
  35. #
  36. # Wrapped by Johan Vromans <jv@mh.nl> on Sat Feb  3 21:36:57 1990
  37. #
  38. # This archive contains:
  39. #    README        dusage.1    dusage.pl    
  40. #
  41. # Error checking via wc(1) will be performed.
  42.  
  43. LANG=""; export LANG
  44.  
  45. echo x - README
  46. cat >README <<'@EOF'
  47. Guarding disk space is one of the problems of system management. 
  48.  
  49. Some time ago I converted an old awk/sed/sh script to keep track of
  50. disk usage to a new perl program, added new features, options; even
  51. wrote a manual page.
  52.  
  53. Using a list of pathnames, this program filters the output of du(1) to
  54. find the amount of disk space used for each of the paths (actually, it
  55. collects all values in one single du run). It adds the new value to
  56. the list, shifting old values up. It then generates a nice report of
  57. the amount of disk space occupied in each of the specified paths,
  58. together with the amount it grew (or shrinked) since the previous run,
  59. and since 7 runs ago. When run daily, this gives daily and weekly
  60. figures.
  61.  
  62. # This program requires perl version 3.0, patchlevel 4 or higher.
  63.  
  64. # Copyright 1990 Johan Vromans, all rights reserved.
  65. # Peaceware. This program may be used, modified and distributed as long as
  66. # this copyright notice remains part of the source. It may not be sold, or 
  67. # be used to harm any living creature including the world and the universe.
  68. @EOF
  69. set `wc -lwc <README`
  70. if test $1$2$3 != 211931064
  71. then
  72.     echo ERROR: wc results of README are $* should be 21 193 1064
  73. fi
  74.  
  75. chmod 644 README
  76.  
  77. echo x - dusage.1
  78. cat >dusage.1 <<'@EOF'
  79. .TH DUSAGE 1
  80. .SH NAME
  81. dusage \- provide disk usage statistics
  82. .SH SYNOPSIS
  83. .B dusage
  84. .RB [ \-afghruD ]
  85. .RI "[\fB\-i\fR" " input" ]
  86. .RI "[\fB\-p\fR" " dir" ]
  87. .RI [ "control file" ]
  88. .SH DESCRIPTION
  89. .I Dusage
  90. is a perl script which produces disk usage statistics. These
  91. statistics include the number of blocks, the increment since the previous run
  92. (which is assumed to be yesterday if run daily), and the increment
  93. since 7 runs ago (which could be interpreted as a week if run daily).
  94. .I Dusage
  95. is driven by a 
  96. .IR "control file" ,
  97. which describes the names of the files (directories) to be reported,
  98. and which also contains the results of previous runs.
  99. .PP
  100. When
  101. .I dusage
  102. is run, it reads the
  103. .IR "control file" ,
  104. [optionally] gathers new disk usage values by calling
  105. .IR du (1),
  106. prints the report, and [optionally] updates the
  107. .I control file
  108. with the new information.
  109. .PP
  110. Filenames in the control file may have wildcards. In this case, the
  111. wildcards are expanded, and all entries reported. Both the expanded
  112. names as the wildcard info are maintained in the control file. New
  113. files in these directories will automatically show up, deleted files
  114. will disappear when they have run out of data in the control file (but
  115. see the 
  116. .B \-r
  117. option).
  118. .br
  119. Wildcard expansion only adds filenames which are not already on the list.
  120. .PP
  121. The control file may also contain filenames preceded with an
  122. exclamation mark ``!''; these entries are skipped. This is meaningful
  123. in conjunction with wildcards, to exclude entries which result from a
  124. wildcard expansion.
  125. .PP
  126. The control file may have lines starting with a dash ``\-'',
  127. which causes the report to start on a new page. Any text following the
  128. dash is placed in the page header, immediately following the text
  129. ``Disk usage statistics''.
  130. .PP
  131. The available command line options are:
  132. .TP 5
  133. .B \-D
  134. Turns on debugging, which yields lots of trace information.
  135. .TP
  136. .B \-a
  137. Reports the statistics for this and all previous runs, as opposed to
  138. the normal case, which is to generate the statistics for this run, and
  139. the differences between the previous and 7th previous run.
  140. .TP
  141. .B \-f
  142. Reports file statistics also. Default is to only report directories.
  143. .TP
  144. .B \-g
  145. Gathers new data by calling 
  146. .IR du (1).
  147. .TP
  148. .B \-h
  149. Provides a help message. No work is done.
  150. .TP
  151. .BI \-i " input"
  152. Uses
  153. .I input
  154. as data obtained by calling
  155. .IR du (1).
  156. .TP
  157. .BI \-p " dir"
  158. All filenames in the control file are interpreted relative to this
  159. directory.
  160. .TP
  161. .B \-r
  162. Retains entries which don't have any data anymore. If this option is
  163. not used, entries without data are not reported, and removed from the
  164. control file.
  165. .TP
  166. .B \-u
  167. Update the control file with new values.
  168. .PP
  169. The default name for the control file is
  170. .BR .du.ctl ,
  171. optionally preceded by the name supplied with the
  172. .B \-p
  173. option.
  174. .SH EXAMPLES
  175. Given the following control file:
  176. .sp
  177. .nf
  178. .ne 3
  179. .in +.5i
  180. \- for manual page
  181. maildir
  182. maildir/*
  183. !maildir/unimportant
  184. src
  185. .in
  186. .fi
  187. .sp
  188. This will generate the following (example) report when running the
  189. command ``dusage -gu controlfile'':
  190. .sp
  191. .nf
  192. .ne 7
  193. .in +.5i
  194. Disk usage statistics for manual page      Wed Jan 10 13:38
  195.  
  196.  blocks    +day     +week  directory
  197. -------  -------  -------  --------------------------------
  198.    6518                    maildir
  199.       2                    maildir/dirent
  200.     498                    src
  201. .in
  202. .fi
  203. .sp
  204. After updating the control file, it will contain:
  205. .sp
  206. .nf
  207. .ne 4
  208. .in +.5i
  209. \- for manual page
  210. maildir 6518::::::
  211. maildir/dirent  2::::::
  212. maildir/*
  213. !maildir/unimportant
  214. src     498::::::
  215. .in
  216. .fi
  217. .sp
  218. The names in the control file are separated by the values with a TAB;
  219. the values are separated with colons. Also, the entries found by
  220. expanding the wildcard are added. If the wildcard expansion had
  221. generated a name ``maildir/unimportant'' it would have been skipped.
  222. .br
  223. When the program is rerun after one day, it could print the following
  224. report:
  225. .sp
  226. .nf
  227. .ne 7
  228. .in +.5i
  229. Disk usage statistics for manual page      Wed Jan 10 13:38
  230.  
  231.  blocks    +day     +week  directory
  232. -------  -------  -------  --------------------------------
  233.    6524       +6           maildir
  234.       2        0           maildir/dirent
  235.     486      -12           src
  236. .in
  237. .fi
  238. .sp
  239. The control file will contain:
  240. .sp
  241. .nf
  242. .ne 4
  243. .in +.5i
  244. \- for manual page
  245. maildir 6524:6518:::::
  246. maildir/dirent  2:2:::::
  247. maildir/*
  248. !maildir/unimportant
  249. src     486:498:::::
  250. .in
  251. .fi
  252. .sp
  253. It takes very little fantasy to imagine what will happen on subsequent
  254. runs...
  255. .PP
  256. When the contents of the control file are to be changed, e.g. to add
  257. new filenames, a normal text editor can be used. Just add or remove
  258. lines, and they will be taken into account automatically.
  259. .PP
  260. When run without 
  261. .B \-g
  262. or
  263. .B \-u
  264. options, it actually reproduces the report from the previous run.
  265. .PP
  266. When multiple runs are required, save the output of
  267. .IR du (1)
  268. in a file, and pass this file to
  269. .I dusage
  270. using the 
  271. .BI \-i "file"
  272. option.
  273. .SH BUGS
  274. Running the same control file with different values of the 
  275. .B \-f
  276. and
  277. .B \-r
  278. options may cause strange results.
  279. .SH AUTHOR
  280. Johan Vromans, Multihouse Research, Gouda, The Netherlands.
  281. .sp
  282. Send bugs and remarks to <jv@mh.nl> .
  283. @EOF
  284. set `wc -lwc <dusage.1`
  285. if test $1$2$3 != 2048505139
  286. then
  287.     echo ERROR: wc results of dusage.1 are $* should be 204 850 5139
  288. fi
  289.  
  290. chmod 444 dusage.1
  291.  
  292. echo x - dusage.pl
  293. sed 's/^@//' >dusage.pl <<'@EOF'
  294. #!/usr/bin/perl
  295.  
  296. # This program requires perl version 3.0, patchlevel 4 or higher.
  297.  
  298. # Copyright 1990 Johan Vromans, all rights reserved.
  299. # Peaceware. This program may be used, modified and distributed as long as
  300. # this copyright notice remains part of the source. It may not be sold, or 
  301. # be used to harm any living creature including the world and the universe.
  302.  
  303. $my_name = $0;
  304.  
  305. ################ usage ################
  306.  
  307. sub usage {
  308.   local ($help) = shift (@_);
  309.   local ($usg) = "usage: $my_name [-afghruD][-i input][-p dir] ctlfile";
  310.   die "$usg\nstopped" unless $help;
  311.   print STDERR "$usg\n";
  312.   print STDERR <<EndOfHelp
  313.  
  314.     -D          - provide debugging info
  315.     -a          - provide all statis
  316.     -f          - also report file statistics
  317.     -g          - gather new data
  318.     -h          - this help message
  319.     -i input    - input data as obtained by 'du dir' [def = 'du dir']
  320.     -p dir      - path to which files in the control file are relative
  321.     -r          - do not discard entries which don't have data
  322.     -u          - update the control file with new values
  323.     ctlfile     - file which controls which dirs to report [def = dir/.du.ctl]
  324. EndOfHelp
  325.   ;
  326.   exit 1;
  327. }
  328.  
  329. ################ main stream ################
  330.  
  331. &do_get_options;        # process options
  332. &do_parse_ctl;            # read the control file
  333. &do_gather if $gather;        # gather new info
  334. &do_report_and_update;        # report and update
  335.  
  336. ################ end of main stream ################
  337.  
  338. ################ other subroutines ################
  339.  
  340. sub do_get_options {
  341.  
  342.   # Default values for options
  343.  
  344.   $debug = 0;
  345.   $noupdate = 1;
  346.   $retain = 0;
  347.   $gather = 0;
  348.   $allfiles = 0;
  349.   $allstats = 0;
  350.  
  351.   # Command line options. We use a modified version of getopts.pl.
  352.  
  353.   &usage (0) if &Getopts ("Dafghi:p:ru");
  354.   &usage (1) if $opt_h;
  355.   &usage (0) if $#ARGV > 0;
  356.  
  357.   $debug    |= $opt_D if defined $opt_D;    # -D -> debug
  358.   $allstats |= $opt_a if defined $opt_a;    # -a -> all stats
  359.   $allfiles |= $opt_f if defined $opt_f;    # -f -> report all files
  360.   $gather   |= $opt_g if defined $opt_g;    # -g -> gather new data
  361.   $retain   |= $opt_r if defined $opt_r;    # -r -> retain old entries
  362.   $noupdate = !$opt_u if defined $opt_u;    # -u -> update the control file
  363.   $du        = $opt_i if defined $opt_i;    # -i input file
  364.   if ( defined $opt_p ) {            # -p path
  365.     $root = $opt_p;
  366.     $root = $` while ($root =~ m|/$|);
  367.     $prefix = "$root/";
  368.     $root = "/" if $root eq "";
  369.   }
  370.   else {
  371.     $prefix = $root = "";
  372.   }
  373.   $table    = ($#ARGV == 0) ? shift (@ARGV) : "$prefix.du.ctl";
  374.   $runtype = $allfiles ? "file" : "directory";
  375.   if ($debug) {
  376.     print STDERR "@(#)@ dusage    1.7 - dusage.pl\n";
  377.     print STDERR "Options:";
  378.     print STDERR " debug" if $debug;    # silly, isn't it...
  379.     print STDERR $noupdate ? " no" : " ", "update";
  380.     print STDERR $retain ? " " : " no", "retain";
  381.     print STDERR $gather ? " " : " no", "gather";
  382.     print STDERR $allstats ? " " : " no", "allstats";
  383.     print STDERR "\n";
  384.     print STDERR "Root = $root [prefix = $prefix]\n";
  385.     print STDERR "Control file = $table\n";
  386.     print STDERR "Input data = $du\n" if defined $du;
  387.     print STDERR "Run type = $runtype\n";
  388.     print STDERR "\n";
  389.   }
  390. }
  391.  
  392. sub do_parse_ctl {
  393.  
  394.   # Parsing the control file.
  395.   #
  396.   # This file contains the names of the (sub)directories to tally,
  397.   # and the values dereived from previous runs.
  398.   # The names of the directories are relative to the $root.
  399.   # The name may contain '*' or '?' characters, and will be globbed if so.
  400.   # An entry starting with ! is excluded.
  401.   #
  402.   # To add a new dir, just add the name. The special name '.' may 
  403.   # be used to denote the $root directory. If used, '-p' must be
  404.   # specified.
  405.   #
  406.   # Upon completion:
  407.   #  - %oldblocks is filled with the previous values,
  408.   #    colon separated, for each directory.
  409.   #  - @targets contains a list of names to be looked for. These include
  410.   #    break indications and globs info, which will be stripped from
  411.   #    the actual search list.
  412.  
  413.   open (tb, "<$table") || die "Cannot open control file $table, stopped";
  414.   @targets = ();
  415.   %oldblocks = ();
  416.   %newblocks = ();
  417.  
  418.   while ($tb = <tb>) {
  419.     chop ($tb);
  420.  
  421.     # preferred syntax: <dir><TAB><size>:<size>:....
  422.     # allowable          <dir><TAB><size> <size> ...
  423.     # possible          <dir>
  424.  
  425.     if ( $tb =~ /^-/ ) {    # break
  426.       push (@targets, "$tb");
  427.       printf STDERR "tb: *break* $tb\n" if $debug;
  428.       next;
  429.     }
  430.  
  431.     if ( $tb =~ /^!/ ) {    # exclude
  432.       $excl = $';        #';
  433.       @a = grep ($_ ne $excl, @targets);
  434.       @targets = @a;
  435.       push (@targets, "*$tb");
  436.       printf STDERR "tb: *excl* $tb\n" if $debug;
  437.       next;
  438.     }
  439.  
  440.     if ($tb =~ /^(.+)\t([\d: ]+)/) {
  441.       $name = $1;
  442.       @blocks = split (/[ :]/, $2);
  443.     }
  444.     else {
  445.       $name = $tb;
  446.       @blocks = ("","","","","","","","");
  447.     }
  448.  
  449.     if ($name eq ".") {
  450.       if ( $root eq "" ) {
  451.     printf STDERR "Warning: \".\" in control file w/o \"-p path\" - ignored\n";
  452.     next;
  453.       }
  454.       $name = $root;
  455.     } else {
  456.       $name = $prefix . $name unless ord($name) == ord ("/");
  457.     }
  458.  
  459.     # Check for globs ...
  460.     if ( $gather && $name =~ /\*|\?/ ) {
  461.       print STDERR "glob: $name\n" if $debug;
  462.       foreach $n ( <${name}> ) {
  463.     next unless $allfiles || -d $n;
  464.     # Globs never overwrite existing entries
  465.     if ( !defined $oldblocks{$n} ) {
  466.       $oldblocks{$n} = ":::::::";
  467.       push (@targets, $n);
  468.     }
  469.     printf STDERR "glob: -> $n\n" if $debug;
  470.       }
  471.       # Put on the globs list, and terminate this entry
  472.       push (@targets, "*$name");
  473.       next;
  474.     }
  475.  
  476.     push (@targets, "$name");
  477.     # Entry may be rewritten (in case of globs)
  478.     $oldblocks{$name} = join (":", @blocks[0..7]);
  479.  
  480.     print STDERR "tb: $name\t$oldblocks{$name}\n" if $debug;
  481.   }
  482.   close (tb);
  483. }
  484.  
  485. sub do_gather {
  486.  
  487.   # Build a targets match string, and an optimized list of directories to
  488.   # search.
  489.   $targets = "//";
  490.   @list = ();
  491.   $last = "///";
  492.   foreach $name (sort (@targets)) {
  493.     next if $name =~ /^[-*]/;
  494.     next unless $allfiles || -d $name;
  495.     $targets .= "$name//"; 
  496.     next if ($name =~ m|^$last/|);
  497.     push (@list, $name);
  498.     $last = $name;
  499.   }
  500.  
  501.   print STDERR "targets: $targets\n" if $debug;
  502.   print STDERR "list: @list\n" if $debug;
  503.   print STDERR "reports: @targets\n" if $debug;
  504.  
  505.   $du = "du " . ($allfiles ? "-a" : "") . " @list|"
  506.     unless defined $du; # in which case we have a data file
  507.  
  508.   # Process the data. If a name is found in the target list, 
  509.   # %newblocks will be set to the new blocks value.
  510.  
  511.   open (du, "$du") || die "Cannot get data from $du, stopped";
  512.   while ($du = <du>) {
  513.     chop ($du);
  514.     ($blocks,$name) = split (/\t/, $du);
  515.     if (($i = index ($targets, "//$name//")) >= 0) {
  516.       # tally and remove entry from search list
  517.       $newblocks{$name} = $blocks;
  518.       print STDERR "du: $name $blocks\n" if $debug;
  519.       substr ($targets, $i, length($name) + 2) = "";
  520.     }
  521.   }
  522.   close (du);
  523. }
  524.  
  525.  
  526. # Report generation
  527.  
  528. format std_hdr =
  529. Disk usage statistics@<<<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<
  530. $subtitle, $date
  531.  
  532.  blocks    +day     +week  @<<<<<<<<<<<<<<<
  533. $runtype
  534. -------  -------  -------  --------------------------------
  535. @.
  536. format std_out =
  537. @@>>>>>> @>>>>>>> @>>>>>>>  ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<..
  538. $blocks, $d_day, $d_week, $name
  539. @.
  540.  
  541. format all_hdr =
  542. Disk usage statistics@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<           @<<<<<<<<<<<<<<<
  543. $subtitle, $date
  544.  
  545.  --0--    --1--    --2--    --3--    --4--    --5--    --6--    --7--   @<<<<<<<<<<<<<<<
  546. $runtype
  547. -------  -------  -------  -------  -------  -------  -------  -------  --------------------------------
  548. @.
  549. format all_out =
  550. @@>>>>>> @>>>>>>> @>>>>>>> @>>>>>>> @>>>>>>> @>>>>>>> @>>>>>>> @>>>>>>>  ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<..
  551. $a[0],  $a[1],   $a[2],   $a[3],   $a[4],   $a[5],   $a[6],   $a[7],    $name
  552. @.
  553.  
  554. sub do_report_and_update {
  555.  
  556.   # Prepare update of the control file
  557.   if ( !$noupdate ) {
  558.     if ( !open (tb, ">$table") ) {
  559.       print STDERR "Warning: cannot update control file $table - continuing\n";
  560.       $noupdate = 1;
  561.     }
  562.   }
  563.  
  564.   if ( $allstats ) {
  565.     $^ = "all_hdr";
  566.     $~ = "all_out";
  567.   }
  568.   else {
  569.     $^ = "std_hdr";
  570.     $~ = "std_out";
  571.   }
  572.   $date = `date`;
  573.   chop ($date);
  574.  
  575.   # In one pass the report is generated, and the control file rewritten.
  576.  
  577.   foreach $name (@targets) {
  578.     if ($name =~ /^-/ ) {
  579.       $subtitle = $';                #';
  580.       print tb "$name\n" unless $noupdate;
  581.       print STDERR "tb: $name\n" if $debug;
  582.       $- = -1;
  583.       next;
  584.     }
  585.     if ($name  =~ /^\*$prefix/ ) {
  586.       print tb "$'\n" unless $noupdate;        #';
  587.       print STDERR "tb: $'\n" if $debug;    #';
  588.       next;
  589.     }
  590.     @a = split (/:/, $oldblocks{$name});
  591.     unshift (@a, $newblocks{$name}) if $gather;
  592.     $name = "." if $name eq $root;
  593.     $name = $' if $name =~ /^$prefix/;        #';
  594.     if ($#a < 0) {    # no data?
  595.       if ($retain) {
  596.     @a = ("","","","","","","","");
  597.       }
  598.       else {
  599.     # Discard
  600.     print STDERR "--: $name\n" if $debug;
  601.     next;
  602.       }
  603.     }
  604.     print STDERR "Warning: ", 1+$#a, " entries for $name\n"
  605.       if ($debug && $#a != 8);
  606.     $line = "$name\t" . join(":",@a[0..7]) . "\n";
  607.     print tb $line unless $noupdate;
  608.     print STDERR "tb: $line" if $debug;
  609.  
  610.     $blocks = $a[0];
  611.     if ( !$allstats ) {
  612.       $d_day = $d_week = "";
  613.       if ($blocks ne "") {
  614.     if ($a[1] ne "") {        # dayly delta
  615.       $d_day = $blocks - $a[1];
  616.       $d_day = "+" . $d_day if $d_day > 0;
  617.     }
  618.     if ($a[7] ne "") {        # weekly delta
  619.       $d_week = $blocks - $a[7];
  620.       $d_week = "+" . $d_week if $d_week > 0;
  621.     }
  622.       }
  623.     }
  624.     write;
  625.   }
  626.  
  627.   # Close control file, if opened
  628.   close (tb) unless $noupdate;
  629. }
  630.  
  631. # Modified version of getopts ...
  632.  
  633. sub Getopts {
  634.     local($argumentative) = @_;
  635.     local(@args,$_,$first,$rest);
  636.     local($opterr) = 0;
  637.  
  638.     @args = split( / */, $argumentative );
  639.     while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
  640.     ($first,$rest) = ($1,$2);
  641.     $pos = index($argumentative,$first);
  642.     if($pos >= $[) {
  643.         if($args[$pos+1] eq ':') {
  644.         shift(@ARGV);
  645.         if($rest eq '') {
  646.             $rest = shift(@ARGV);
  647.         }
  648.         eval "\$opt_$first = \$rest;";
  649.         }
  650.         else {
  651.         eval "\$opt_$first = 1";
  652.         if($rest eq '') {
  653.             shift(@ARGV);
  654.         }
  655.         else {
  656.             $ARGV[0] = "-$rest";
  657.         }
  658.         }
  659.     }
  660.     else {
  661.         print stderr "Unknown option: $first\n";
  662.         $opterr++;
  663.         if($rest ne '') {
  664.         $ARGV[0] = "-$rest";
  665.         }
  666.         else {
  667.         shift(@ARGV);
  668.         }
  669.     }
  670.     }
  671.     return $opterr;
  672. }
  673. @EOF
  674. set `wc -lwc <dusage.pl`
  675. if test $1$2$3 != 379157810356
  676. then
  677.     echo ERROR: wc results of dusage.pl are $* should be 379 1578 10356
  678. fi
  679.  
  680. chmod 444 dusage.pl
  681.  
  682. exit 0
  683. --
  684. Johan Vromans                       jv@mh.nl via internet backbones
  685. Multihouse Automatisering bv               uucp: ..!{uunet,hp4nl}!mh.nl!jv
  686. Doesburgweg 7, 2803 PL Gouda, The Netherlands  phone/fax: +31 1820 62944/62500
  687. ------------------------ "Arms are made for hugging" -------------------------
  688.  
  689.  
  690.