aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xaddemup125
1 files changed, 125 insertions, 0 deletions
diff --git a/addemup b/addemup
new file mode 100755
index 0000000..b8b73ba
--- /dev/null
+++ b/addemup
@@ -0,0 +1,125 @@
+#!/usr/bin/perl -w
+
+use POSIX 'round';
+use Getopt::Std;
+$Getopt::Std::STANDARD_HELP_VERSION++;
+
+$VERSION = "0.0.1";
+($SELF = $0) =~ s,.*/,,;
+
+sub VERSION_MESSAGE {
+ print "$SELF $VERSION\n";
+}
+
+sub HELP_MESSAGE {
+ print <<EOF;
+$SELF - add up numbers in stdin, files, or command line args; print total.
+
+Usage: $SELF [-a] [-i] [-m] [args ...]
+
+By default, lines are read from standard input or the files given as
+arguments. All numbers (integers, floating point, or hexadecimal with
+leading 0x) found in the input are added together, and the total is
+printed to stdout.
+
+Options:
+ -a Treats the arguments as numbers to add, rather than filenames.
+ -i Integers only. Any fractional part is truncated (use -r to round).
+ -m Also print the mean (average) of the numbers read.
+ -M Print only the mean (not the total).
+ -r Round to nearest integer (implies -i).
+ -h, --help
+ Print this usage message and exit.
+ -v, --version
+ Print version number and exit.
+
+Written by B. Watson <urchlay\@slackware.uk>, licensed WTFPL.
+EOF
+}
+
+# This regular expression doesn't recognize numbers without
+# a digit before the decimal point, such as .01 or -.123. However,
+# before this is used, the caller puts a 0 in the right place for
+# such numbers.
+# Doesn't the regex syntax with /x and comments remind you of
+# assembly language?
+$re = qr {
+ ( # EITHER:
+ (?: # hex number,
+ 0[xX] # leading 0x or 0X required,
+ [0-9a-fA-F]+ # hex digits, case insensitive.
+ )
+ | # OR:
+ (?: # decimal number,
+ [-+]? # with optional leading sign,
+ \d+ # at least one digit required left of the decimal point,
+ (?:\.\d+)? # optional decimal point with trailing digits,
+ (?: # MAYBE:
+ [eE] # optional "e" or "E" for scientific notation,
+ [-+]? # optional exponent sign,
+ \d+ # at least one exponent digit,
+ )? # ...but the whole exponent part is optional!
+ )
+ )
+}x;
+
+getopts('aihmMrv', \%opt) || exit 1;
+
+VERSION_MESSAGE(), exit(0) if $opt{v};
+HELP_MESSAGE(), exit(0) if $opt{h};
+
+$opt{i} = 1 if $opt{r};
+
+$sum = $count = 0;
+
+if($opt{a}) {
+ for(@ARGV) {
+ $sum += $_ for extract_numbers();
+ }
+} else {
+ while(<>) {
+ $sum += $_ for extract_numbers();
+ }
+}
+
+print $sum . "\n" unless $opt{M};
+if($opt{m} || $opt{M}) {
+ if($count) {
+ $avg = $sum / $count;
+ $avg = to_int($avg) if $opt{i};
+ } else {
+ # always print an average, even if no numbers were read.
+ $avg = 0;
+ }
+ print $avg . "\n";
+}
+exit 0;
+
+###
+# Extract all the numbers in $_, return them an in array.
+sub extract_numbers {
+ my @ret;
+
+ # Handle numbers like .12 or -.99 by inserting a 0 before the .
+ s/(^|\D)(\.\d)/$1 . "0" . $2/ge;
+
+ for(/$re/gc) {
+ $_ = hex $_ if /^0[xX]/;
+ $_ = to_int($_) if $opt{i};
+ push @ret, $_;
+ $count++;
+ }
+
+ return @ret;
+}
+
+sub to_int {
+ warn "was: $_\n";
+ if($opt{r}) {
+ $_ = round($_);
+ } else {
+ $_ = int $_;
+ }
+ warn "now: $_\n";
+ return $_;
+}