diff --git a/externals/cowsay/ChangeLog b/externals/cowsay/ChangeLog new file mode 100644 index 0000000000..b28ef91b85 --- /dev/null +++ b/externals/cowsay/ChangeLog @@ -0,0 +1,31 @@ +3.03 28 May 1999 + - Added cows/tux.cow, as suggested by xmanoel@i.am + - Compatibility with 5.6.0, due to a change in qw(). + - Renamed devil.cow to daemon.cow, since I know better. :-) + +3.02 04 November 1999 + - Fixed boneheaded code placement so that cowsay -l actually works. + +3.01 01 November 1999 + - Fixed compatibility issues between the Text::Wrap module + that changed between 5.005_02 and 5.005_03. + - Fixed tab expansion issues with Text::Tabs. + +3.0 13 April 1999, released 14 August 1999 + - Rewritten into Perl 5 and presented to the world. + +-- Not present in CVS from here on down -- + +2.x Date? + - Arbitrary messages. + - Figlet support (-n). + - Line wrap length (-w). + - Multiple pre-set expressions. + - Better arg parsing loop. + - Message from stdin or command line. + +1.0 Date? + - SUBJECT is VERB OBJECT + +$Id: ChangeLog,v 1.4 2000/05/29 17:55:24 tony Exp $ +This file is part of cowsay. (c) 1999-2000 Tony Monroe. diff --git a/externals/cowsay/INSTALL b/externals/cowsay/INSTALL new file mode 100644 index 0000000000..d3844ca565 --- /dev/null +++ b/externals/cowsay/INSTALL @@ -0,0 +1,15 @@ +================= +Installing cowsay +================= + +If you really want to get things installed a nice and pretty way, + + sh install.sh + +It will ask approximately one question. If you can't answer it, +you need serious help. + +If the install goes well, you can start cowing immediately! Just +be sure to read the manual page first... + +$Id: INSTALL,v 1.1 1999/08/14 08:03:17 tony Exp $ diff --git a/externals/cowsay/LICENSE b/externals/cowsay/LICENSE new file mode 100644 index 0000000000..944770492d --- /dev/null +++ b/externals/cowsay/LICENSE @@ -0,0 +1,36 @@ +============== +cowsay License +============== + +cowsay is distributed under the same licensing terms as Perl: the +Artistic License or the GNU General Public License. If you don't +want to track down these licenses and read them for yourself, use +the parts that I'd prefer: + +(0) I wrote it and you didn't. + +(1) Give credit where credit is due if you borrow the code for some +other purpose. + +(2) If you have any bugfixes or suggestions, please notify me so +that I may incorporate them. + +(3) If you try to make money off of cowsay, you suck. + +=============== +cowsay Legalese +=============== + +(0) Copyright (c) 1999 Tony Monroe. All rights reserved. All +lefts may or may not be reversed at my discretion. + +(1) This software package can be freely redistributed or modified +under the terms described above in the "cowsay License" section +of this file. + +(2) cowsay is provided "as is," with no warranties whatsoever, +expressed or implied. If you want some implied warranty about +merchantability and/or fitness for a particular purpose, you will +not find it here, because there is no such thing here. + +(3) I hate legalese. diff --git a/externals/cowsay/MANIFEST b/externals/cowsay/MANIFEST new file mode 100644 index 0000000000..a3ff805fed --- /dev/null +++ b/externals/cowsay/MANIFEST @@ -0,0 +1,11 @@ +ChangeLog Changes to recent versions. +INSTALL Instructions for installing cowsay. +LICENSE The license for use and redistribution of cowsay. +MANIFEST This file. +README Read this first. Really. +Wrap.pm.diff Diff for Text/Wrap.pm. +cows/* Support files used by cowsay. +cowsay Main cowsay executable. +cowsay.1 Main cowsay manual page. +install.sh cowsay installation script. +pgp_public_key.txt Verify the signature file with this key. diff --git a/externals/cowsay/README b/externals/cowsay/README new file mode 100644 index 0000000000..b5de08c405 --- /dev/null +++ b/externals/cowsay/README @@ -0,0 +1,38 @@ +=========== +cowsay 3.03 +=========== + +cowsay is a configurable talking cow, written in Perl. It operates +much as the figlet program does, and it written in the same spirit +of silliness. + +cowsay is actually a pretty old program. It has not really been +released before, and I am releasing it in the hope that someone +other than myself will be amused by it. + +The first major version of cowsay had one cow and one message +template: $foo is $verb $bar. Not very flexible, but people managed +to do pretty interesting things with it. The second major version +scrapped many of the limitations of the first, by allowing arbitrary +messages, multiple cowfiles, and even support for cows talking in +figlet. The third version was a rewrite of the second into Perl +5, whereupon the code got a lot smaller and more manageable. :-) + +If you are using Perl 5.004, you may have problems with Text::Wrap. +(Yeesh, this module changes more than it should...) I've included +a diff for the Text::Wrap (version 97.011701) that is shipped with +5.004_04; the concept is simple enough that even older Perls can +take advantage of this silly little patch; if there is a "sub fill" +in the documentation for the module, copy it to a more useful +section of that file. If not, just take "sub fill" wholesale from +the patch. Oh, and consider upgrading to 5.005_03 or later. +Please. You'll like it, I promise. + +To install cowsay, consult the INSTALL file in this directory. + +For the terms and conditions of use, consult the LICENSE file in +this directory. + +-- Tony Monroe (tony@nog.net) + +$Id: README,v 1.3 2000/05/28 06:24:46 tony Exp $ diff --git a/externals/cowsay/Wrap.pm.diff b/externals/cowsay/Wrap.pm.diff new file mode 100644 index 0000000000..1da064079e --- /dev/null +++ b/externals/cowsay/Wrap.pm.diff @@ -0,0 +1,47 @@ +*** Wrap.pm.in Thu May 22 00:21:42 1997 +--- Wrap.pm Fri Nov 12 10:00:15 1999 +*************** +*** 3,9 **** + require Exporter; + + @ISA = (Exporter); +! @EXPORT = qw(wrap); + @EXPORT_OK = qw($columns); + + $VERSION = 97.011701; +--- 3,9 ---- + require Exporter; + + @ISA = (Exporter); +! @EXPORT = qw(wrap fill); + @EXPORT_OK = qw($columns); + + $VERSION = 97.011701; +*************** +*** 66,71 **** +--- 66,90 ---- + + print "-----------$r---------\n" if $debug;; + return $r; ++ } ++ ++ ## Copied up from below. ++ sub fill ++ { ++ my ($ip, $xp, @raw) = @_; ++ my @para; ++ my $pp; ++ ++ for $pp (split(/\n\s+/, join("\n",@raw))) { ++ $pp =~ s/\s+/ /g; ++ my $x = wrap($ip, $xp, $pp); ++ push(@para, $x); ++ } ++ ++ # if paragraph_indent is the same as line_indent, ++ # separate paragraphs with blank lines ++ ++ return join ($ip eq $xp ? "\n\n" : "\n", @para); + } + + 1; diff --git a/externals/cowsay/cows/bunny.cow b/externals/cowsay/cows/bunny.cow new file mode 100644 index 0000000000..869933f72a --- /dev/null +++ b/externals/cowsay/cows/bunny.cow @@ -0,0 +1,10 @@ +## +## A cute little wabbit +## +$the_cow = <> 5.4 +## +$the_cow = < \\ _ -~ `. ^-` ^-_ + ///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~ + /.-~ +EOC diff --git a/externals/cowsay/cows/elephant.cow b/externals/cowsay/cows/elephant.cow new file mode 100644 index 0000000000..959cd3f6dd --- /dev/null +++ b/externals/cowsay/cows/elephant.cow @@ -0,0 +1,15 @@ +## +## An elephant out and about +## +$the_cow = < < > .---. + $thoughts | \\ \\ - ~ ~ - / / | + _____ ..-~ ~-..-~ + | | \\~~~\\.' `./~~~/ + --------- \\__/ \\__/ + .' O \\ / / \\ " + (_____, `._.' | } \\/~~~/ + `----. / } | / \\__/ + `-. | / | / `. ,~~| + ~-.__| /_ - ~ ^| /- _ `..-' + | / | / ~-. `-. _ _ _ + |_____| |_____| ~ - . _ _ _ _ _> +EOC diff --git a/externals/cowsay/cows/supermilker.cow b/externals/cowsay/cows/supermilker.cow new file mode 100644 index 0000000000..1424f9e668 --- /dev/null +++ b/externals/cowsay/cows/supermilker.cow @@ -0,0 +1,10 @@ +## +## A cow being milked, probably from Lars Smith (lars@csua.berkeley.edu) +## +$the_cow = <____) >___ ^\\_\\_\\_\\_\\_\\_\\) + ^^^//\\\\_^^//\\\\_^ ^(\\_\\_\\_\\) + ^^^ ^^ ^^^ ^ +EOC diff --git a/externals/cowsay/cows/turtle.cow b/externals/cowsay/cows/turtle.cow new file mode 100644 index 0000000000..4286b0948a --- /dev/null +++ b/externals/cowsay/cows/turtle.cow @@ -0,0 +1,20 @@ +## +## A mysterious turtle... +## +$the_cow = < 'oo', + 'f' => 'default.cow', + 'n' => 0, + 'T' => ' ', + 'W' => 40, +); + +getopts('bde:f:ghlLnNpstT:wW:y', \%opts); + +&display_usage if $opts{'h'}; +&list_cowfiles if $opts{'l'}; + +$borg = $opts{'b'}; +$dead = $opts{'d'}; +$greedy = $opts{'g'}; +$paranoid = $opts{'p'}; +$stoned = $opts{'s'}; +$tired = $opts{'t'}; +$wired = $opts{'w'}; +$young = $opts{'y'}; +$eyes = substr($opts{'e'}, 0, 2); +$tongue = substr($opts{'T'}, 0, 2); +$the_cow = ""; + +&slurp_input; +$Text::Wrap::columns = $opts{'W'}; +@message = ($opts{'n'} ? expand(@message) : + split("\n", fill("", "", @message))); +&construct_balloon; +&construct_face; +&get_cow; +print @balloon_lines; +print $the_cow; + +sub list_cowfiles { + my $basedir; + my @dirfiles; + chop($basedir = cwd); + for my $d (split(/$pathsep/, $cowpath)) { + print "Cow files in $d:\n"; + opendir(COWDIR, $d) || die "$0: Cannot open $d\n"; + for my $file (readdir COWDIR) { + if ($file =~ s/\.cow$//) { + push(@dirfiles, $file); + } + } + closedir(COWDIR); + print wrap("", "", sort @dirfiles), "\n"; + @dirfiles = (); + chdir($basedir); + } + exit(0); +} + +sub slurp_input { + unless ($ARGV[0]) { + chomp(@message = ); + } else { + &display_usage if $opts{'n'}; + @message = join(' ', @ARGV); + } +} + +sub maxlength { + my ($l, $m); + $m = -1; + for my $i (@_) { + $l = length $i; + $m = $l if ($l > $m); + } + return $m; +} + +sub construct_balloon { + my $max = &maxlength(@message); + my $max2 = $max + 2; ## border space fudge. + my $format = "%s %-${max}s %s\n"; + my @border; ## up-left, up-right, down-left, down-right, left, right + if ($0 =~ /think/i) { + $thoughts = 'o'; + @border = qw[ ( ) ( ) ( ) ]; + } elsif (@message < 2) { + $thoughts = '\\'; + @border = qw[ < > ]; + } else { + $thoughts = '\\'; + if ($V and $V gt v5.6.0) { # Thanks, perldelta. + @border = qw[ / \\ \\ / | | ]; + } else { + @border = qw[ / \ \ / | | ]; + } + } + push(@balloon_lines, + " " . ("_" x $max2) . " \n" , + sprintf($format, $border[0], $message[0], $border[1]), + (@message < 2 ? "" : + map { sprintf($format, $border[4], $_, $border[5]) } + @message[1 .. $#message - 1]), + (@message < 2 ? "" : + sprintf($format, $border[2], $message[$#message], $border[3])), + " " . ("-" x $max2) . " \n" + ); +} + +sub construct_face { + if ($borg) { $eyes = "=="; } + if ($dead) { $eyes = "xx"; $tongue = "U "; } + if ($greedy) { $eyes = "\$\$"; } + if ($paranoid) { $eyes = "@@"; } + if ($stoned) { $eyes = "**"; $tongue = "U "; } + if ($tired) { $eyes = "--"; } + if ($wired) { $eyes = "OO"; } + if ($young) { $eyes = ".."; } +} + +sub get_cow { +## +## Get a cow from the specified cowfile; otherwise use the default cow +## which was defined above in $the_cow. +## + my $f = $opts{'f'}; + my $full = ""; + if ($opts{'f'} =~ m,/,) { + $full = $opts{'f'}; + } else { + for my $d (split(/:/, $cowpath)) { + if (-f "$d/$f") { + $full = "$d/$f"; + last; + } elsif (-f "$d/$f.cow") { + $full = "$d/$f.cow"; + last; + } + } + if ($full eq "") { + die "$progname: Could not find $f cowfile!\n"; + } + } + do $full; + die "$progname: $@\n" if $@; +} + +sub display_usage { + die </dev/null 2>&1; then + echo Found a good perl in $perl + goodperls="$goodperls $perl" + fi +done +echo The following perl executables will run cowsay: +echo $goodperls +echo I recommend the latest stable perl you can find. +set $goodperls +if [ -z "$1" ]; then + echo Ack! You do not have Perl 5 installed correctly! + echo Get thee to CPAN! + exit 1 +fi +usethisperl=$1 +echo I will be using $1 because I know it will work. + +echo Now I need an installation prefix. I will use /usr/local unless +printf "you give me a better idea here: " +if [ -n "$backdoor" ]; then + prefix=$backdoor + printf "%s (specified on command line)\n" $prefix +else + read prefix +fi + +PREFIX=${prefix:-/usr/local} + +echo Okay, time to install this puppy. + +echo s,%BANGPERL%,!$usethisperl,\; > install.pl +echo s,%PREFIX%,$PREFIX,\; >> install.pl +set -x +mkdir -p $PREFIX/bin || (mkdir $PREFIX; mkdir $PREFIX/bin) +$usethisperl -p install.pl cowsay > $PREFIX/bin/cowsay +chmod a+x $PREFIX/bin/cowsay +ln -s cowsay $PREFIX/bin/cowthink +mkdir -p $PREFIX/man/man1 || ($mkdir $PREFIX; mkdir $PREFIX/man; mkdir $PREFIX/man/man1) +$usethisperl -p install.pl cowsay.1 > $PREFIX/man/man1/cowsay.1 +chmod a+r $PREFIX/man/man1/cowsay.1 +ln -s cowsay.1 $PREFIX/man/man1/cowthink.1 +mkdir -p $PREFIX/share/cows || (mkdir $PREFIX; mkdir $PREFIX/share; mkdir $PREFIX/share/cows) +tar -cf - $filelist | (cd $PREFIX/share && tar -xvf -) +set +x + +echo Okay, let us see if the install actually worked. + +if [ ! -f $PREFIX/share/cows/default.cow ]; then + echo The default cow file did not make it across! + echo Ooops, it failed...sorry! + exit 1 +fi + +echo Installation complete! Enjoy the cows! diff --git a/externals/cowsay/pgp_public_key.txt b/externals/cowsay/pgp_public_key.txt new file mode 100644 index 0000000000..135735e6f9 --- /dev/null +++ b/externals/cowsay/pgp_public_key.txt @@ -0,0 +1,14 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.2 + +mQCNAzNS+H4AAAEEAMilCcxLMIqMNXhZoeHjbeJGnHoKP0JpKDejz66qBlDwP+du +FvVBHkyNIuU3orKe7l/aXIR8TcpjrxdcmrjjzwuB1aV48V4swT7v9UXvv9YP41Pd +1pXYXNelXHmE0eKSfcnkkFmYTJBXPaPNTJ3rpZpZOEW3SfgrrheqQm6w/6IJAAUR +tBpUb255IE1vbnJvZSA8dG9ueUBub2cubmV0PrQdVG9ueSBNb25yb2UgPHRtb25y +b2VAbm9nLm5ldD6JAJUDBRA2bDA5F6pCbrD/ogkBASOZA/9XtYSpcPjpi62MTEZl +l+wV1svtlUlPvKkDE7FB9bwnMbF1FgGhTe/H4/8qDu20m5eGxMX58Ri7HPXWjjJ/ +CrUWMzGVbg1JBkgh+d9QvEywmR0j4WezaifW1nCbxk0GrW1PjJyGb3kx7vrIF1Km +xt6VwxTgfJzjIioBJFb4EiDyaLQnVG9ueSBNb25yb2UgPHRtb25yb2VAQ1NVQS5C +ZXJrZWxleS5FRFU+ +=WpM4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/resources/cows/builtin/companion.cow b/resources/cows/builtin/companion.cow new file mode 100644 index 0000000000..e3514cda6f --- /dev/null +++ b/resources/cows/builtin/companion.cow @@ -0,0 +1,9 @@ + $thoughts + $thoughts + /---\__/---\\ + | / .... \ || + \ ..--.. // + |..($eyes). || + / ..--.. \\ + | \ .... / || + \---/--\---// diff --git a/resources/cows/custom/README b/resources/cows/custom/README new file mode 100644 index 0000000000..d0c28b6a56 --- /dev/null +++ b/resources/cows/custom/README @@ -0,0 +1,2 @@ +You can add custom ".cow" files here to make them available to the "cowsay" +interpreter in Remarkup. diff --git a/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php b/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php index d1c4de1154..66801d9578 100644 --- a/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php +++ b/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php @@ -1,60 +1,68 @@ markupError( - pht( - 'Unable to locate the `%s` binary. Install cowsay.', - 'cowsay')); - } - - $bin = idx($argv, 'think') ? 'cowthink' : 'cowsay'; + $action = idx($argv, 'think') ? 'think' : 'say'; $eyes = idx($argv, 'eyes', 'oo'); $tongue = idx($argv, 'tongue', ' '); - $cow = idx($argv, 'cow', 'default'); - - // NOTE: Strip this aggressively to prevent nonsense like - // `cow=/etc/passwd`. We could build a whiltelist with `cowsay -l`. - $cow = preg_replace('/[^a-z.-]+/', '', $cow); - - $future = new ExecFuture( - '%s -e %s -T %s -f %s ', - $bin, - $eyes, - $tongue, - $cow); - - $future->setTimeout(15); - $future->write($content); - - list($err, $stdout, $stderr) = $future->resolve(); - - if ($err) { - return $this->markupError( - pht( - 'Execution of `%s` failed: %s', - 'cowsay', - $stderr)); + + $map = self::getCowMap(); + + $cow = idx($argv, 'cow'); + $cow = phutil_utf8_strtolower($cow); + if (empty($map[$cow])) { + $cow = 'default'; } + $result = id(new PhutilCowsay()) + ->setTemplate($map[$cow]) + ->setAction($action) + ->setEyes($eyes) + ->setTongue($tongue) + ->setText($content) + ->renderCow(); if ($this->getEngine()->isTextMode()) { - return $stdout; + return $result; } return phutil_tag( 'div', array( 'class' => 'PhabricatorMonospaced remarkup-cowsay', ), - $stdout); + $result); + } + + private static function getCowMap() { + $root = dirname(phutil_get_library_root('phabricator')); + + $directories = array( + $root.'/externals/cowsay/cows/', + $root.'/resources/cows/builtin/', + $root.'/resources/cows/custom/', + ); + + $map = array(); + foreach ($directories as $directory) { + foreach (Filesystem::listDirectory($directory, false) as $cow_file) { + $matches = null; + if (!preg_match('/^(.*)\.cow\z/', $cow_file, $matches)) { + continue; + } + $cow_name = $matches[1]; + $cow_name = phutil_utf8_strtolower($cow_name); + $map[$cow_name] = Filesystem::readFile($directory.$cow_file); + } + } + + return $map; } }