| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Astro::App::Satpass2; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 20 |  |  | 20 |  | 2076 | use 5.008; | 
|  | 20 |  |  |  |  | 73 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 20 |  |  | 20 |  | 120 | use strict; | 
|  | 20 |  |  |  |  | 36 |  | 
|  | 20 |  |  |  |  | 518 |  | 
| 6 | 20 |  |  | 20 |  | 103 | use warnings; | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 699 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 20 |  |  | 20 |  | 9481 | use Astro::App::Satpass2::Locale qw{ __localize }; | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 1224 |  | 
| 9 | 20 |  |  | 20 |  | 9201 | use Astro::App::Satpass2::Macro::Command; | 
|  | 20 |  |  |  |  | 57 |  | 
|  | 20 |  |  |  |  | 665 |  | 
| 10 | 20 |  |  | 20 |  | 8662 | use Astro::App::Satpass2::Macro::Code; | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 661 |  | 
| 11 | 20 |  |  | 20 |  | 9560 | use Astro::App::Satpass2::ParseTime; | 
|  | 20 |  |  |  |  | 95 |  | 
|  | 20 |  |  |  |  | 945 |  | 
| 12 | 20 |  |  |  |  | 3948 | use Astro::App::Satpass2::Utils qw{ | 
| 13 |  |  |  |  |  |  | :ref | 
| 14 |  |  |  |  |  |  | __arguments __legal_options | 
| 15 |  |  |  |  |  |  | expand_tilde find_package_pod | 
| 16 |  |  |  |  |  |  | has_method instance load_package | 
| 17 |  |  |  |  |  |  | my_dist_config quoter | 
| 18 |  |  |  |  |  |  | __parse_class_and_args | 
| 19 | 20 |  |  | 20 |  | 168 | }; | 
|  | 20 |  |  |  |  | 45 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 20 |  |  | 20 |  | 18946 | use Astro::Coord::ECI 0.077;			# This needs at least 0.049. | 
|  | 20 |  |  |  |  | 285274 |  | 
|  | 20 |  |  |  |  | 818 |  | 
| 22 | 20 |  |  | 20 |  | 10602 | use Astro::Coord::ECI::Moon 0.077; | 
|  | 20 |  |  |  |  | 125299 |  | 
|  | 20 |  |  |  |  | 842 |  | 
| 23 | 20 |  |  | 20 |  | 10451 | use Astro::Coord::ECI::Star 0.077; | 
|  | 20 |  |  |  |  | 185871 |  | 
|  | 20 |  |  |  |  | 887 |  | 
| 24 | 20 |  |  | 20 |  | 11074 | use Astro::Coord::ECI::Sun 0.077; | 
|  | 20 |  |  |  |  | 77946 |  | 
|  | 20 |  |  |  |  | 811 |  | 
| 25 | 20 |  |  | 20 |  | 31985 | use Astro::Coord::ECI::TLE 0.077 qw{:constants}; # This needs at least 0.059. | 
|  | 20 |  |  |  |  | 1158068 |  | 
|  | 20 |  |  |  |  | 5573 |  | 
| 26 | 20 |  |  | 20 |  | 13101 | use Astro::Coord::ECI::TLE::Set 0.077; | 
|  | 20 |  |  |  |  | 57159 |  | 
|  | 20 |  |  |  |  | 826 |  | 
| 27 |  |  |  |  |  |  | # The following includes @CARP_NOT. | 
| 28 | 20 |  |  | 20 |  | 180 | use Astro::Coord::ECI::Utils 0.112 qw{ :all };	# This needs at least 0.112. | 
|  | 20 |  |  |  |  | 738 |  | 
|  | 20 |  |  |  |  | 9509 |  | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | { | 
| 31 |  |  |  |  |  |  | local $@ = undef; | 
| 32 | 20 |  | 50 |  |  | 74 | use constant HAVE_TLE_IRIDIUM	=> eval { | 
| 33 |  |  |  |  |  |  | require Astro::Coord::ECI::TLE::Iridium; | 
| 34 |  |  |  |  |  |  | Astro::Coord::ECI::TLE::Iridium->VERSION( 0.077 ); | 
| 35 |  |  |  |  |  |  | 1; | 
| 36 | 20 |  |  | 20 |  | 174 | } || 0; | 
|  | 20 |  |  |  |  | 43 |  | 
| 37 |  |  |  |  |  |  | # Unfortunately the alias code creates the alias even if the version | 
| 38 |  |  |  |  |  |  | # is unacceptable; so we may have to just delete the Iridium aliases | 
| 39 |  |  |  |  |  |  | unless( HAVE_TLE_IRIDIUM ) { | 
| 40 |  |  |  |  |  |  | my %type_map = Astro::Coord::ECI::TLE->alias(); | 
| 41 |  |  |  |  |  |  | foreach my $name ( keys %type_map ) { | 
| 42 |  |  |  |  |  |  | $type_map{$name} eq 'Astro::Coord::ECI::TLE::Iridium' | 
| 43 |  |  |  |  |  |  | and Astro::Coord::ECI::TLE->alias( $name, undef ); | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  | } | 
| 47 |  |  |  |  |  |  |  | 
| 48 | 20 |  |  | 20 |  | 12058 | use Attribute::Handlers; | 
|  | 20 |  |  |  |  | 103899 |  | 
|  | 20 |  |  |  |  | 159 |  | 
| 49 | 20 |  |  | 20 |  | 722 | use Clone (); | 
|  | 20 |  |  |  |  | 63 |  | 
|  | 20 |  |  |  |  | 325 |  | 
| 50 | 20 |  |  | 20 |  | 101 | use Cwd (); | 
|  | 20 |  |  |  |  | 41 |  | 
|  | 20 |  |  |  |  | 396 |  | 
| 51 | 20 |  |  | 20 |  | 136 | use File::Glob qw{ :glob }; | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 5058 |  | 
| 52 | 20 |  |  | 20 |  | 163 | use File::HomeDir; | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 1111 |  | 
| 53 | 20 |  |  | 20 |  | 124 | use File::Spec; | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 706 |  | 
| 54 | 20 |  |  | 20 |  | 16720 | use File::Temp; | 
|  | 20 |  |  |  |  | 228085 |  | 
|  | 20 |  |  |  |  | 1627 |  | 
| 55 | 20 |  |  | 20 |  | 202 | use Getopt::Long 2.33; | 
|  | 20 |  |  |  |  | 319 |  | 
|  | 20 |  |  |  |  | 678 |  | 
| 56 | 20 |  |  | 20 |  | 2814 | use IO::File 1.14; | 
|  | 20 |  |  |  |  | 424 |  | 
|  | 20 |  |  |  |  | 2864 |  | 
| 57 | 20 |  |  | 20 |  | 143 | use IO::Handle; | 
|  | 20 |  |  |  |  | 54 |  | 
|  | 20 |  |  |  |  | 751 |  | 
| 58 | 20 |  |  | 20 |  | 150 | use POSIX qw{ floor }; | 
|  | 20 |  |  |  |  | 98 |  | 
|  | 20 |  |  |  |  | 191 |  | 
| 59 | 20 |  |  | 20 |  | 1645 | use Scalar::Util 1.26 qw{ blessed isdual openhandle }; | 
|  | 20 |  |  |  |  | 393 |  | 
|  | 20 |  |  |  |  | 1099 |  | 
| 60 | 20 |  |  | 20 |  | 14407 | use Text::Abbrev; | 
|  | 20 |  |  |  |  | 946 |  | 
|  | 20 |  |  |  |  | 1123 |  | 
| 61 | 20 |  |  | 20 |  | 163 | use Text::ParseWords ();	# Used only for {level1} stuff. | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 409 |  | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 20 |  |  | 20 |  | 100 | use constant ASTRO_SPACETRACK_VERSION => 0.105; | 
|  | 20 |  |  |  |  | 79 |  | 
|  | 20 |  |  |  |  | 1268 |  | 
| 64 | 20 |  |  | 20 |  | 170 | use constant DEFAULT_STDOUT_LAYERS	=> ':encoding(utf-8)'; | 
|  | 20 |  |  |  |  | 48 |  | 
|  | 20 |  |  |  |  | 1844 |  | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | BEGIN { | 
| 67 |  |  |  |  |  |  | eval { | 
| 68 | 20 | 50 |  |  |  | 191 | load_package( 'Time::y2038' ) | 
| 69 |  |  |  |  |  |  | and Time::y2038->import(); | 
| 70 | 20 |  |  |  |  | 1331 | 1; | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 20 | 50 |  | 20 |  | 94 | or do { | 
| 73 | 0 |  |  |  |  | 0 | require Time::Local; | 
| 74 | 0 |  |  |  |  | 0 | Time::Local->import(); | 
| 75 |  |  |  |  |  |  | }; | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | # The following is returned by method _attribute_value() when a | 
| 79 |  |  |  |  |  |  | # non-existent attribute is specified. We can't use undef for this, | 
| 80 |  |  |  |  |  |  | # because the attribute might really be undef. | 
| 81 |  |  |  |  |  |  | # NOTE that this used to be just bless \( $x = undef ) ..., but blead | 
| 82 |  |  |  |  |  |  | # Perl 6a011f13d7690dbe2e03ad7500756c983bcb1834 did not like this | 
| 83 |  |  |  |  |  |  | # (modificatoin of read-only variable). | 
| 84 | 20 |  |  |  |  | 78 | use constant NULL	=> do { | 
| 85 | 20 |  |  |  |  | 54 | my $x = undef; | 
| 86 | 20 |  |  |  |  | 1686 | bless \$x, 'Null'; | 
| 87 | 20 |  |  | 20 |  | 152 | }; | 
|  | 20 |  |  |  |  | 75 |  | 
| 88 |  |  |  |  |  |  | # The canonical way to see if $rslt actually contains the above is | 
| 89 |  |  |  |  |  |  | # NULL_REF eq ref $rslt | 
| 90 | 20 |  |  | 20 |  | 165 | use constant NULL_REF	=> ref NULL; | 
|  | 20 |  |  |  |  | 40 |  | 
|  | 20 |  |  |  |  | 1131 |  | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 20 |  |  | 20 |  | 127 | use constant SUN_CLASS_DEFAULT	=> 'Astro::Coord::ECI::Sun'; | 
|  | 20 |  |  |  |  | 40 |  | 
|  | 20 |  |  |  |  | 9438 |  | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | our $VERSION = '0.051'; | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | # The following 'cute' code is so that we do not determine whether we | 
| 97 |  |  |  |  |  |  | # actually have optional modules until we really need them, and yet do | 
| 98 |  |  |  |  |  |  | # not repeat the process once it is done. | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | my $have_time_hires; | 
| 101 |  |  |  |  |  |  | $have_time_hires = sub { | 
| 102 |  |  |  |  |  |  | my $value = load_package( 'Time::HiRes' ); | 
| 103 |  |  |  |  |  |  | $have_time_hires = sub { return $value }; | 
| 104 |  |  |  |  |  |  | return $value; | 
| 105 |  |  |  |  |  |  | }; | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | my $have_astro_spacetrack; | 
| 108 |  |  |  |  |  |  | $have_astro_spacetrack = sub { | 
| 109 |  |  |  |  |  |  | my $value = load_package( { lib => undef }, 'Astro::SpaceTrack' ) && eval { | 
| 110 |  |  |  |  |  |  | Astro::SpaceTrack->VERSION( ASTRO_SPACETRACK_VERSION ); | 
| 111 |  |  |  |  |  |  | 1; | 
| 112 |  |  |  |  |  |  | }; | 
| 113 |  |  |  |  |  |  | $have_astro_spacetrack = sub { $value }; | 
| 114 |  |  |  |  |  |  | return $value; | 
| 115 |  |  |  |  |  |  | }; | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | my $default_geocoder; | 
| 118 |  |  |  |  |  |  | $default_geocoder = sub { | 
| 119 |  |  |  |  |  |  | my $value = | 
| 120 |  |  |  |  |  |  | _can_use_geocoder( 'Astro::App::Satpass2::Geocode::OSM' | 
| 121 |  |  |  |  |  |  | ); | 
| 122 |  |  |  |  |  |  | $default_geocoder = sub { return $value }; | 
| 123 |  |  |  |  |  |  | return $value; | 
| 124 |  |  |  |  |  |  | }; | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | sub _can_use_geocoder { | 
| 127 | 0 |  |  | 0 |  | 0 | my ( $geocoder ) = @_; | 
| 128 | 0 | 0 |  |  |  | 0 | my $pkg = load_package( $geocoder ) | 
| 129 |  |  |  |  |  |  | or return; | 
| 130 | 0 | 0 |  |  |  | 0 | load_package( $pkg->GEOCODER_CLASS() ) | 
| 131 |  |  |  |  |  |  | or return; | 
| 132 | 0 |  |  |  |  | 0 | return $pkg; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | my $interrupted = 'Interrupted by user.'; | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | my %twilight_def = ( | 
| 138 |  |  |  |  |  |  | civil => deg2rad (-6), | 
| 139 |  |  |  |  |  |  | nautical => deg2rad (-12), | 
| 140 |  |  |  |  |  |  | astronomical => deg2rad (-18), | 
| 141 |  |  |  |  |  |  | ); | 
| 142 |  |  |  |  |  |  | my %twilight_abbr = abbrev (keys %twilight_def); | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | #	Individual commands are defined by subroutines of the same name, | 
| 145 |  |  |  |  |  |  | #	and having the Verb attribute. You can specify additional | 
| 146 |  |  |  |  |  |  | #	attributes if you need to. Following are descriptions of the | 
| 147 |  |  |  |  |  |  | #	attributes used by  this script. | 
| 148 |  |  |  |  |  |  | # | 
| 149 |  |  |  |  |  |  | #	Configure(configurations) | 
| 150 |  |  |  |  |  |  | # | 
| 151 |  |  |  |  |  |  | #	The 'Configure' attribute specifies options to be passed to | 
| 152 |  |  |  |  |  |  | #	Getopt::Long::Configure before the options are parsed. For | 
| 153 |  |  |  |  |  |  | #	example, if a command wants to keep unrecognized options on the | 
| 154 |  |  |  |  |  |  | #	command you would specify: | 
| 155 |  |  |  |  |  |  | #	    sub foo : Configure(pass_through) Verb | 
| 156 |  |  |  |  |  |  | # | 
| 157 |  |  |  |  |  |  | #	Tokenize(options) | 
| 158 |  |  |  |  |  |  | # | 
| 159 |  |  |  |  |  |  | #	The 'Tokenize' attribute specifies tokenizatino options. These | 
| 160 |  |  |  |  |  |  | #	can not take effect until fairly late in the parse when the | 
| 161 |  |  |  |  |  |  | #	tokens are known. These options are parsed by Getopt::Long, and | 
| 162 |  |  |  |  |  |  | #	the value of the attribute is a reference to the options hash | 
| 163 |  |  |  |  |  |  | #	thus generated. Possible options are: | 
| 164 |  |  |  |  |  |  | #	  -expand_tilde - Expand tildes in the tokens. For historical | 
| 165 |  |  |  |  |  |  | #		reasons this is the default, but it can be negated by | 
| 166 |  |  |  |  |  |  | #		specifying -noexpand_tilde. Tildes in redirect | 
| 167 |  |  |  |  |  |  | #		specifications are always expanded. | 
| 168 |  |  |  |  |  |  | # | 
| 169 |  |  |  |  |  |  | #	Tweak(options) | 
| 170 |  |  |  |  |  |  | # | 
| 171 |  |  |  |  |  |  | #	The 'Tweak' attribute specifies miscellaneous tweaks to | 
| 172 |  |  |  |  |  |  | #	subroutine usage. Possible options are: | 
| 173 |  |  |  |  |  |  | #	  -unsatisfied - Execute even inside an unsatisfied if(). | 
| 174 |  |  |  |  |  |  | #		Subroutines with this attribute may have to be aware | 
| 175 |  |  |  |  |  |  | #		that they are being called within the scope of an | 
| 176 |  |  |  |  |  |  | #		unsatisfied if(). All interactive methods that must be | 
| 177 |  |  |  |  |  |  | #		called even inside an unsatisfied if() MUST have this | 
| 178 |  |  |  |  |  |  | #		attribute. These are begin() and end(), and anything | 
| 179 |  |  |  |  |  |  | #		that might dispatch either of these. At the moment this | 
| 180 |  |  |  |  |  |  | #		means if() and time(). | 
| 181 |  |  |  |  |  |  | #	  -completion - Requires as argument the name of the command | 
| 182 |  |  |  |  |  |  | #	        completion method. This can not be checked at compile | 
| 183 |  |  |  |  |  |  | #	        time. It will be called with the following arguments: | 
| 184 |  |  |  |  |  |  | #	        $code - the relevant code reference | 
| 185 |  |  |  |  |  |  | #	        $text - the text being completed | 
| 186 |  |  |  |  |  |  | #	        $line - the line being completed | 
| 187 |  |  |  |  |  |  | #	        $start - the current position in the line. | 
| 188 |  |  |  |  |  |  | #	        It should return either a reference to an array | 
| 189 |  |  |  |  |  |  | #	        containing possible completions, or nothing to fall | 
| 190 |  |  |  |  |  |  | #	        through to standard completion | 
| 191 |  |  |  |  |  |  | # | 
| 192 |  |  |  |  |  |  | #	Verb(options) | 
| 193 |  |  |  |  |  |  | # | 
| 194 |  |  |  |  |  |  | #	The 'Verb' attribute identifies the subroutine as representing a | 
| 195 |  |  |  |  |  |  | #	cvsx command. If it has options, they should be specified inside | 
| 196 |  |  |  |  |  |  | #	parentheses as a whitespace-separated list of option | 
| 197 |  |  |  |  |  |  | #	specifications appropriate for Getopt::Long. For example: | 
| 198 |  |  |  |  |  |  | #	    sub foo : Verb(bar baz=s) | 
| 199 |  |  |  |  |  |  | #	specifies that 'foo' is a command, taking options -bar, and | 
| 200 |  |  |  |  |  |  | #	-baz; the latter takes a string value. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | { | 
| 203 |  |  |  |  |  |  | my %attr; | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | sub Configure : ATTR(CODE,RAWDATA) { | 
| 206 | 0 |  |  | 0 | 0 | 0 | my ( undef, undef, $code, $name, $data ) = @_; | 
| 207 | 0 |  |  |  |  | 0 | $attr{$code}{$name} = _attr_list( $data ); | 
| 208 | 0 |  |  |  |  | 0 | return; | 
| 209 | 20 |  |  | 20 |  | 171 | } | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 166 |  | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub Tokenize : ATTR(CODE,RAWDATA) { | 
| 212 | 19 |  |  | 19 | 0 | 27361 | my ( undef, undef, $code, $name, $data ) = @_; | 
| 213 | 19 |  |  |  |  | 136 | my $opt = _attr_hash( $name, $data, qw{ expand_tilde|expand-tilde! } ); | 
| 214 |  |  |  |  |  |  | exists $opt->{expand_tilde} | 
| 215 | 19 | 50 |  |  |  | 184 | or $opt->{expand_tilde} = 1; | 
| 216 | 19 |  |  |  |  | 106 | $attr{$code}{$name} = $opt; | 
| 217 | 19 |  |  |  |  | 79 | return; | 
| 218 | 20 |  |  | 20 |  | 23825 | } | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 110 |  | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | sub Tweak : ATTR(CODE,RAWDATA) { | 
| 221 | 266 |  |  | 266 | 0 | 7401 | my ( undef, undef, $code, $name, $data ) = @_; | 
| 222 | 266 |  |  |  |  | 636 | $attr{$code}{$name} = _attr_hash( $name, $data, | 
| 223 |  |  |  |  |  |  | qw{ completion=s unsatisfied! } ); | 
| 224 | 266 |  |  |  |  | 744 | return; | 
| 225 | 20 |  |  | 20 |  | 20712 | } | 
|  | 20 |  |  |  |  | 69 |  | 
|  | 20 |  |  |  |  | 97 |  | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub Verb : ATTR(CODE,RAWDATA) { | 
| 228 | 1239 |  |  | 1239 | 0 | 2229913 | my ( undef, undef, $code, $name, $data ) = @_; | 
| 229 | 1239 |  |  |  |  | 2631 | $attr{$code}{$name} = _attr_list( $data ); | 
| 230 | 1239 |  |  |  |  | 3449 | return; | 
| 231 | 20 |  |  | 20 |  | 20007 | } | 
|  | 20 |  |  |  |  | 75 |  | 
|  | 20 |  |  |  |  | 130 |  | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | sub _attr_hash { | 
| 234 | 285 |  |  | 285 |  | 757 | my ( $name, $arg, @legal ) = @_; | 
| 235 | 285 |  |  |  |  | 1009 | my $gol = Getopt::Long::Parser->new(); | 
| 236 | 285 |  |  |  |  | 5210 | my %opt; | 
| 237 |  |  |  |  |  |  | $gol->getoptionsfromarray( | 
| 238 |  |  |  |  |  |  | _attr_list( $arg ), | 
| 239 |  |  |  |  |  |  | \%opt, | 
| 240 |  |  |  |  |  |  | @legal, | 
| 241 | 285 | 50 |  |  |  | 560 | ) or do { | 
| 242 | 0 |  |  |  |  | 0 | require Carp; | 
| 243 | 0 |  |  |  |  | 0 | Carp::croak( "Bad $name option" ); | 
| 244 |  |  |  |  |  |  | }; | 
| 245 | 285 |  |  |  |  | 94612 | return \%opt; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | sub _attr_list { | 
| 249 | 1524 | 50 |  | 1524 |  | 4271 | defined( local $_ = $_[0] ) | 
| 250 |  |  |  |  |  |  | or return []; | 
| 251 | 1524 |  |  |  |  | 5556 | s/ \A \s+ //smx; | 
| 252 | 1524 |  |  |  |  | 15843 | return [ split qr< \s+ >smx ]; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | sub __get_attr { | 
| 256 | 1235 |  |  | 1235 |  | 2997 | my ( undef, $code, $name, $dflt ) = @_;	# $pkg unused | 
| 257 | 1235 | 50 |  |  |  | 2610 | defined $code | 
| 258 |  |  |  |  |  |  | or return \%attr; | 
| 259 |  |  |  |  |  |  | defined $name | 
| 260 | 1235 | 50 |  |  |  | 2231 | or return $attr{$code}; | 
| 261 |  |  |  |  |  |  | exists $attr{$code}{$name} | 
| 262 | 1235 | 100 |  |  |  | 5306 | and return $attr{$code}{$name}; | 
| 263 | 628 |  |  |  |  | 2654 | return $dflt; | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | my %mutator = ( | 
| 268 |  |  |  |  |  |  | almanac_horizon	=> \&_set_almanac_horizon, | 
| 269 |  |  |  |  |  |  | appulse => \&_set_angle, | 
| 270 |  |  |  |  |  |  | autoheight => \&_set_unmodified, | 
| 271 |  |  |  |  |  |  | backdate => \&_set_unmodified, | 
| 272 |  |  |  |  |  |  | background => \&_set_unmodified, | 
| 273 |  |  |  |  |  |  | continuation_prompt => \&_set_unmodified, | 
| 274 |  |  |  |  |  |  | country => \&_set_unmodified, | 
| 275 |  |  |  |  |  |  | date_format => \&_set_formatter_attribute, | 
| 276 |  |  |  |  |  |  | desired_equinox_dynamical => \&_set_formatter_attribute, | 
| 277 |  |  |  |  |  |  | debug => \&_set_unmodified, | 
| 278 |  |  |  |  |  |  | echo => \&_set_unmodified, | 
| 279 |  |  |  |  |  |  | edge_of_earths_shadow => \&_set_unmodified, | 
| 280 |  |  |  |  |  |  | ellipsoid => \&_set_ellipsoid, | 
| 281 |  |  |  |  |  |  | error_out => \&_set_unmodified, | 
| 282 |  |  |  |  |  |  | events	=> \&_set_unmodified, | 
| 283 |  |  |  |  |  |  | exact_event => \&_set_unmodified, | 
| 284 |  |  |  |  |  |  | execute_filter => \&_set_code_ref,	# Undocumented and unsupported | 
| 285 |  |  |  |  |  |  | explicit_macro_delete => \&_set_unmodified, | 
| 286 |  |  |  |  |  |  | extinction => \&_set_unmodified, | 
| 287 |  |  |  |  |  |  | filter => \&_set_unmodified, | 
| 288 |  |  |  |  |  |  | flare_mag_day => \&_set_unmodified, | 
| 289 |  |  |  |  |  |  | flare_mag_night => \&_set_unmodified, | 
| 290 |  |  |  |  |  |  | formatter => \&_set_formatter, | 
| 291 |  |  |  |  |  |  | geocoder => \&_set_geocoder, | 
| 292 |  |  |  |  |  |  | geometric => \&_set_unmodified, | 
| 293 |  |  |  |  |  |  | gmt => \&_set_formatter_attribute, | 
| 294 |  |  |  |  |  |  | height => \&_set_distance_meters, | 
| 295 |  |  |  |  |  |  | horizon => \&_set_angle, | 
| 296 |  |  |  |  |  |  | illum	=> \&_set_illum_class, | 
| 297 |  |  |  |  |  |  | latitude => \&_set_angle_or_undef, | 
| 298 |  |  |  |  |  |  | local_coord => \&_set_formatter_attribute, | 
| 299 |  |  |  |  |  |  | location => \&_set_unmodified, | 
| 300 |  |  |  |  |  |  | longitude => \&_set_angle_or_undef, | 
| 301 |  |  |  |  |  |  | model => \&_set_model, | 
| 302 |  |  |  |  |  |  | max_mirror_angle => \&_set_angle, | 
| 303 |  |  |  |  |  |  | output_layers	=> \&_set_output_layers, | 
| 304 |  |  |  |  |  |  | pass_threshold => \&_set_angle_or_undef, | 
| 305 |  |  |  |  |  |  | pass_variant	=> \&_set_pass_variant, | 
| 306 |  |  |  |  |  |  | perltime => \&_set_time_parser_attribute, | 
| 307 |  |  |  |  |  |  | prompt => \&_set_unmodified, | 
| 308 |  |  |  |  |  |  | refraction	=> \&_set_unmodified, | 
| 309 |  |  |  |  |  |  | simbad_url => \&_set_unmodified, | 
| 310 |  |  |  |  |  |  | singleton => \&_set_unmodified, | 
| 311 |  |  |  |  |  |  | spacetrack => \&_set_spacetrack, | 
| 312 |  |  |  |  |  |  | stdout => \&_set_stdout, | 
| 313 |  |  |  |  |  |  | sun	=> \&_set_sun_class,		# Only in {level1} | 
| 314 |  |  |  |  |  |  | time_format => \&_set_formatter_attribute, | 
| 315 |  |  |  |  |  |  | time_formatter => \&_set_formatter_attribute, | 
| 316 |  |  |  |  |  |  | time_parser => \&_set_time_parser, | 
| 317 |  |  |  |  |  |  | ##    timing => \&_set_unmodified, | 
| 318 |  |  |  |  |  |  | twilight => \&_set_twilight,  # 'civil', 'nautical', 'astronomical' | 
| 319 |  |  |  |  |  |  | # (or a unique abbreviation thereof), | 
| 320 |  |  |  |  |  |  | # or degrees above (positive) or below | 
| 321 |  |  |  |  |  |  | # (negative) the geometric horizon. | 
| 322 |  |  |  |  |  |  | tz => \&_set_tz, | 
| 323 |  |  |  |  |  |  | verbose => \&_set_unmodified, # 0 = events only | 
| 324 |  |  |  |  |  |  | # 1 = whenever above horizon | 
| 325 |  |  |  |  |  |  | # 2 = anytime | 
| 326 |  |  |  |  |  |  | visible => \&_set_unmodified, # 1 = only if sun down & sat illuminated | 
| 327 |  |  |  |  |  |  | warning => \&_set_warner_attribute,	# True to warn/die; false to carp/croak. | 
| 328 |  |  |  |  |  |  | warn_on_empty => \&_set_unmodified, | 
| 329 |  |  |  |  |  |  | # True to have list commands warn on | 
| 330 |  |  |  |  |  |  | # an empty list. | 
| 331 |  |  |  |  |  |  | webcmd => \&_set_webcmd,	# Command to spawn for web pages | 
| 332 |  |  |  |  |  |  | ); | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | my %accessor = ( | 
| 335 |  |  |  |  |  |  | date_format => \&_get_formatter_attribute, | 
| 336 |  |  |  |  |  |  | desired_equinox_dynamical => \&_get_formatter_attribute, | 
| 337 |  |  |  |  |  |  | geocoder => \&_get_geocoder, | 
| 338 |  |  |  |  |  |  | gmt => \&_get_formatter_attribute, | 
| 339 |  |  |  |  |  |  | local_coord => \&_get_formatter_attribute, | 
| 340 |  |  |  |  |  |  | perltime => \&_get_time_parser_attribute, | 
| 341 |  |  |  |  |  |  | spacetrack => \&_get_spacetrack, | 
| 342 |  |  |  |  |  |  | time_format => \&_get_formatter_attribute, | 
| 343 |  |  |  |  |  |  | time_formatter	=> \&_get_formatter_attribute, | 
| 344 |  |  |  |  |  |  | tz => \&_get_time_parser_attribute, | 
| 345 |  |  |  |  |  |  | warning => \&_get_warner_attribute, | 
| 346 |  |  |  |  |  |  | ); | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | foreach ( keys %mutator, qw{ initfile } ) { | 
| 349 |  |  |  |  |  |  | $accessor{$_} ||= sub { return $_[0]->{$_[1]} }; | 
| 350 |  |  |  |  |  |  | } | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | my %shower = ( | 
| 353 |  |  |  |  |  |  | date_format => \&_show_formatter_attribute, | 
| 354 |  |  |  |  |  |  | desired_equinox_dynamical => \&_show_formatter_attribute, | 
| 355 |  |  |  |  |  |  | formatter	=> \&_show_copyable, | 
| 356 |  |  |  |  |  |  | geocoder	=> \&_show_copyable, | 
| 357 |  |  |  |  |  |  | gmt => \&_show_formatter_attribute, | 
| 358 |  |  |  |  |  |  | local_coord => \&_show_formatter_attribute, | 
| 359 |  |  |  |  |  |  | pass_variant	=> \&_show_pass_variant, | 
| 360 |  |  |  |  |  |  | sun		=> \&_show_sun_class,	# only in {level1} | 
| 361 |  |  |  |  |  |  | time_parser => \&_show_time_parser, | 
| 362 |  |  |  |  |  |  | time_format => \&_show_formatter_attribute, | 
| 363 |  |  |  |  |  |  | time_formatter	=> \&_show_formatter_attribute, | 
| 364 |  |  |  |  |  |  | ); | 
| 365 |  |  |  |  |  |  | foreach ( keys %accessor ) { $shower{$_} ||= \&_show_unmodified } | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | #	Attributes which must be set programmatically (i.e. not | 
| 368 |  |  |  |  |  |  | #	interactively or in the initialization file). | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | my %nointeractive = map {$_ => 1} qw{ | 
| 371 |  |  |  |  |  |  | execute_filter | 
| 372 |  |  |  |  |  |  | spacetrack | 
| 373 |  |  |  |  |  |  | stdout | 
| 374 |  |  |  |  |  |  | }; | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | #	Initial object contents | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | my %static = ( | 
| 379 |  |  |  |  |  |  | almanac_horizon	=> 0, | 
| 380 |  |  |  |  |  |  | appulse => 0, | 
| 381 |  |  |  |  |  |  | autoheight => 1, | 
| 382 |  |  |  |  |  |  | background => 1, | 
| 383 |  |  |  |  |  |  | backdate => 0, | 
| 384 |  |  |  |  |  |  | continuation_prompt => '> ', | 
| 385 |  |  |  |  |  |  | date_format => '%a %d-%b-%Y', | 
| 386 |  |  |  |  |  |  | debug => 0, | 
| 387 |  |  |  |  |  |  | echo => 0, | 
| 388 |  |  |  |  |  |  | edge_of_earths_shadow => 1, | 
| 389 |  |  |  |  |  |  | ellipsoid => Astro::Coord::ECI->get ('ellipsoid'), | 
| 390 |  |  |  |  |  |  | error_out => 0, | 
| 391 |  |  |  |  |  |  | events	=> 0, | 
| 392 |  |  |  |  |  |  | exact_event => 1, | 
| 393 |  |  |  |  |  |  | execute_filter => sub { return 1 },	# Undocumented and unsupported | 
| 394 |  |  |  |  |  |  | ##  explicit_macro_delete => 1,			# Deprecated | 
| 395 |  |  |  |  |  |  | extinction => 1, | 
| 396 |  |  |  |  |  |  | filter => 0, | 
| 397 |  |  |  |  |  |  | flare_mag_day => -6, | 
| 398 |  |  |  |  |  |  | flare_mag_night => 0, | 
| 399 |  |  |  |  |  |  | formatter => 'Astro::App::Satpass2::Format::Template',	# Formatter class. | 
| 400 |  |  |  |  |  |  | ##  geocoder => $default_geocoder->(),	# Geocoder class set when accessed | 
| 401 |  |  |  |  |  |  | geometric => 1, | 
| 402 |  |  |  |  |  |  | height => undef,		# meters | 
| 403 |  |  |  |  |  |  | #   initfile => undef,		# Set by init() | 
| 404 |  |  |  |  |  |  | horizon => 20,		# degrees | 
| 405 |  |  |  |  |  |  | illum	=> SUN_CLASS_DEFAULT, | 
| 406 |  |  |  |  |  |  | latitude => undef,		# degrees | 
| 407 |  |  |  |  |  |  | longitude => undef,		# degrees | 
| 408 |  |  |  |  |  |  | max_mirror_angle => HAVE_TLE_IRIDIUM ? rad2deg( | 
| 409 |  |  |  |  |  |  | Astro::Coord::ECI::TLE::Iridium->DEFAULT_MAX_MIRROR_ANGLE ) : | 
| 410 |  |  |  |  |  |  | undef, | 
| 411 |  |  |  |  |  |  | model => 'model', | 
| 412 |  |  |  |  |  |  | #   pending => undef,		# Continued input line if it exists. | 
| 413 |  |  |  |  |  |  | pass_variant	=> PASS_VARIANT_NONE, | 
| 414 |  |  |  |  |  |  | perltime => 0, | 
| 415 |  |  |  |  |  |  | prompt => 'satpass2> ', | 
| 416 |  |  |  |  |  |  | refraction	=> 1, | 
| 417 |  |  |  |  |  |  | simbad_url => 'simbad.u-strasbg.fr', | 
| 418 |  |  |  |  |  |  | singleton => 0, | 
| 419 |  |  |  |  |  |  | #   spacetrack => undef,	# Astro::SpaceTrack object set when accessed | 
| 420 |  |  |  |  |  |  | #   stdout => undef,		# Set to stdout in new(). | 
| 421 |  |  |  |  |  |  | output_layers	=> DEFAULT_STDOUT_LAYERS, | 
| 422 |  |  |  |  |  |  | time_parser => 'Astro::App::Satpass2::ParseTime',	# Time parser class. | 
| 423 |  |  |  |  |  |  | twilight => 'civil', | 
| 424 |  |  |  |  |  |  | tz => $ENV{TZ}, | 
| 425 |  |  |  |  |  |  | verbose => 0, | 
| 426 |  |  |  |  |  |  | visible => 1, | 
| 427 |  |  |  |  |  |  | warning => 0, | 
| 428 |  |  |  |  |  |  | warn_on_empty => 1, | 
| 429 |  |  |  |  |  |  | webcmd => '' | 
| 430 |  |  |  |  |  |  | ); | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | my %sky_class = ( | 
| 433 |  |  |  |  |  |  | fold_case( 'Sun' ) => [ SUN_CLASS_DEFAULT, name => 'Sun' ], | 
| 434 |  |  |  |  |  |  | fold_case( 'Moon' ) => [ 'Astro::Coord::ECI::Moon', name => 'Moon' ], | 
| 435 |  |  |  |  |  |  | #    # The shape of things to come -- maybe | 
| 436 |  |  |  |  |  |  | #    # but commented out because Astro-App-Satpass2 does not depend on | 
| 437 |  |  |  |  |  |  | #    # these | 
| 438 |  |  |  |  |  |  | #    ( map { fold_case( $_ ) => | 
| 439 |  |  |  |  |  |  | #	"Astro::Coord::ECI::VSOP87D::$_" } qw{ Mercury Venus | 
| 440 |  |  |  |  |  |  | #	Mars Jupiter Saturn Uranus Neptune } ), | 
| 441 |  |  |  |  |  |  | ); | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | sub new { | 
| 444 | 7 |  |  | 7 | 1 | 6722 | my ( $class, %args ) = @_; | 
| 445 | 7 | 50 |  |  |  | 39 | ref $class and $class = ref $class; | 
| 446 | 7 |  |  |  |  | 27 | my $self = {}; | 
| 447 | 7 |  |  |  |  | 32 | $self->{bodies} = []; | 
| 448 | 7 |  |  |  |  | 26 | $self->{macro} = {}; | 
| 449 |  |  |  |  |  |  | $self->{sky} = [ | 
| 450 | 7 |  |  |  |  | 87 | SUN_CLASS_DEFAULT->new (), | 
| 451 |  |  |  |  |  |  | Astro::Coord::ECI::Moon->new (), | 
| 452 |  |  |  |  |  |  | ]; | 
| 453 | 7 |  |  |  |  | 3272 | $self->{sky_class} = { %sky_class }; | 
| 454 |  |  |  |  |  |  | $self->{_help_module} = { | 
| 455 | 7 |  |  |  |  | 77 | ''	=> __PACKAGE__, | 
| 456 |  |  |  |  |  |  | eci => 'Astro::Coord::ECI', | 
| 457 |  |  |  |  |  |  | moon => 'Astro::Coord::ECI::Moon', | 
| 458 |  |  |  |  |  |  | set => 'Astro::Coord::ECI::TLE::Set', | 
| 459 |  |  |  |  |  |  | sun => SUN_CLASS_DEFAULT, | 
| 460 |  |  |  |  |  |  | spacetrack => 'Astro::SpaceTrack', | 
| 461 |  |  |  |  |  |  | star => 'Astro::Coord::ECI::Star', | 
| 462 |  |  |  |  |  |  | tle => 'Astro::Coord::ECI::TLE', | 
| 463 |  |  |  |  |  |  | utils => 'Astro::Coord::ECI::Utils', | 
| 464 |  |  |  |  |  |  | }; | 
| 465 |  |  |  |  |  |  | HAVE_TLE_IRIDIUM | 
| 466 | 7 |  |  |  |  | 19 | and $self->{_help_module}{iridium} = 'Astro::Coord::ECI::TLE::Iridium'; | 
| 467 | 7 |  |  |  |  | 22 | bless $self, $class; | 
| 468 | 7 |  |  |  |  | 49 | $self->_frame_push(initial => []); | 
| 469 | 7 |  |  |  |  | 55 | $self->set(stdout => select()); | 
| 470 |  |  |  |  |  |  |  | 
| 471 | 7 |  |  |  |  | 117 | foreach my $name ( keys %static ) { | 
| 472 | 301 | 50 |  |  |  | 747 | exists $args{$name} or $args{$name} = $static{$name}; | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | $self->{_warner} = Astro::App::Satpass2::Warner->new( | 
| 476 |  |  |  |  |  |  | warning => delete $args{warning} | 
| 477 | 7 |  |  |  |  | 120 | ); | 
| 478 |  |  |  |  |  |  |  | 
| 479 | 7 |  |  |  |  | 27 | foreach my $name ( qw{ formatter time_parser } ) { | 
| 480 | 14 |  |  |  |  | 68 | $self->set( $name => delete $args{$name} ); | 
| 481 |  |  |  |  |  |  | } | 
| 482 |  |  |  |  |  |  |  | 
| 483 | 7 |  |  |  |  | 153 | $self->set( %args ); | 
| 484 |  |  |  |  |  |  |  | 
| 485 | 7 |  |  |  |  | 79 | return $self; | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | sub add { | 
| 489 | 1 |  |  | 1 | 1 | 33 | my ( $self, @bodies ) = @_; | 
| 490 | 1 |  |  |  |  | 15 | foreach my $body ( @bodies ) { | 
| 491 | 1 | 50 |  |  |  | 25 | embodies( $body, 'Astro::Coord::ECI::TLE' ) | 
| 492 |  |  |  |  |  |  | or $self->wail( | 
| 493 |  |  |  |  |  |  | 'Arguments must represent Astro::Coord::ECI::TLE objects' ); | 
| 494 |  |  |  |  |  |  | } | 
| 495 | 1 |  |  |  |  | 71 | push @{ $self->{bodies} }, @bodies; | 
|  | 1 |  |  |  |  | 14 |  | 
| 496 | 1 |  |  |  |  | 24 | return $self; | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | sub alias : Verb() { | 
| 500 | 5 |  |  | 5 | 1 | 18 | my ( undef, undef, @args ) = __arguments( @_ );	# Invocant, $opt unused | 
| 501 |  |  |  |  |  |  |  | 
| 502 | 5 | 100 |  |  |  | 18 | if ( @args ) { | 
| 503 | 2 |  |  |  |  | 14 | Astro::Coord::ECI::TLE->alias( @args ); | 
| 504 | 2 |  |  |  |  | 64 | return; | 
| 505 |  |  |  |  |  |  | } else { | 
| 506 | 3 |  |  |  |  | 6 | my $output; | 
| 507 | 3 |  |  |  |  | 16 | my %alias = Astro::Coord::ECI::TLE->alias(); | 
| 508 | 3 |  |  |  |  | 55 | foreach my $key ( sort keys %alias ) { | 
| 509 | 10 |  |  |  |  | 32 | $output .= join( ' ', 'alias', $key, $alias{$key} ) . "\n"; | 
| 510 |  |  |  |  |  |  | } | 
| 511 | 3 |  |  |  |  | 10 | return $output; | 
| 512 |  |  |  |  |  |  | } | 
| 513 | 20 |  |  | 20 |  | 48707 | } | 
|  | 20 |  |  |  |  | 63 |  | 
|  | 20 |  |  |  |  | 129 |  | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | # Attributes must all be on one line to process correctly under Perl | 
| 516 |  |  |  |  |  |  | # 5.8.8. | 
| 517 |  |  |  |  |  |  | sub almanac : Verb( choose=s@ dump! horizon|rise|set! transit! twilight! quarter! ) { | 
| 518 | 3 |  |  | 3 | 1 | 18 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 519 | 3 |  |  |  |  | 35 | $self->_apply_boolean_default( | 
| 520 |  |  |  |  |  |  | $opt, 0, qw{ horizon transit twilight quarter } ); | 
| 521 |  |  |  |  |  |  |  | 
| 522 | 3 |  |  |  |  | 25 | my $almanac_start = $self->__parse_time( | 
| 523 |  |  |  |  |  |  | shift @args, $self->_get_day_midnight()); | 
| 524 | 3 |  | 50 |  |  | 19 | my $almanac_end = $self->__parse_time (shift @args || '+1'); | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 3 | 50 |  |  |  | 13 | $almanac_start >= $almanac_end | 
| 527 |  |  |  |  |  |  | and $self->wail( 'End time must be after start time' ); | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | #	Build an object representing our ground location. | 
| 530 |  |  |  |  |  |  |  | 
| 531 | 3 |  |  |  |  | 15 | my $sta = $self->station(); | 
| 532 |  |  |  |  |  |  |  | 
| 533 | 3 |  |  |  |  | 1211 | my @almanac; | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | #	Iterate through the background bodies, accumulating data or | 
| 536 |  |  |  |  |  |  | #	complaining about the lack of an almanac() method as | 
| 537 |  |  |  |  |  |  | #	appropriate. | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | my @sky = $self->__choose( $opt->{choose}, $self->{sky} ) | 
| 540 | 3 | 50 |  |  |  | 25 | or return $self->__wail( 'No bodies selected' ); | 
| 541 |  |  |  |  |  |  |  | 
| 542 | 3 |  |  |  |  | 10 | foreach my $body ( @sky ) { | 
| 543 | 6 | 50 |  |  |  | 524214 | $body->can ('almanac') or do { | 
| 544 | 0 |  |  |  |  | 0 | $self->whinge( | 
| 545 |  |  |  |  |  |  | ref $body, ' does not support the almanac method'); | 
| 546 | 0 |  |  |  |  | 0 | next; | 
| 547 |  |  |  |  |  |  | }; | 
| 548 |  |  |  |  |  |  | $body->set ( | 
| 549 |  |  |  |  |  |  | station	=> $sta, | 
| 550 |  |  |  |  |  |  | twilight	=> $self->{_twilight}, | 
| 551 | 6 |  |  |  |  | 49 | ); | 
| 552 | 6 |  |  |  |  | 808 | push @almanac, $body->almanac_hash( | 
| 553 |  |  |  |  |  |  | $almanac_start, $almanac_end); | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | # Record number of events found | 
| 557 |  |  |  |  |  |  |  | 
| 558 | 3 |  |  |  |  | 960696 | @almanac = grep { $opt->{$_->{almanac}{event}} } @almanac; | 
|  | 27 |  |  |  |  | 106 |  | 
| 559 | 3 |  |  |  |  | 40 | $self->{events} += @almanac; | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | # Localize the event descriptions if appropriate. | 
| 562 |  |  |  |  |  |  |  | 
| 563 | 3 |  |  |  |  | 19 | _almanac_localize( @almanac ); | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | #	Sort the almanac data by date, and display the results. | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | return $self->__format_data( | 
| 568 |  |  |  |  |  |  | almanac => [ | 
| 569 | 3 |  |  |  |  | 25 | sort { $a->{time} <=> $b->{time} } | 
|  | 41 |  |  |  |  | 112 |  | 
| 570 |  |  |  |  |  |  | @almanac | 
| 571 |  |  |  |  |  |  | ], $opt ); | 
| 572 |  |  |  |  |  |  |  | 
| 573 | 20 |  |  | 20 |  | 11746 | } | 
|  | 20 |  |  |  |  | 78 |  | 
|  | 20 |  |  |  |  | 155 |  | 
| 574 |  |  |  |  |  |  | sub _almanac_localize { | 
| 575 | 9 |  |  | 9 |  | 40 | my @almanac = @_; | 
| 576 | 9 |  |  |  |  | 33 | foreach my $event ( @almanac ) { | 
| 577 |  |  |  |  |  |  | $event->{almanac}{description} = __localize( | 
| 578 |  |  |  |  |  |  | text	=> [ almanac => $event->{body}->get( 'name' ), | 
| 579 |  |  |  |  |  |  | $event->{almanac}{event}, $event->{almanac}{detail} ], | 
| 580 |  |  |  |  |  |  | default	=> $event->{almanac}{description}, | 
| 581 |  |  |  |  |  |  | argument	=> $event->{body}, | 
| 582 | 46 |  |  |  |  | 153 | ); | 
| 583 |  |  |  |  |  |  | } | 
| 584 | 9 |  |  |  |  | 64 | return; | 
| 585 |  |  |  |  |  |  | } | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | sub begin : Verb() Tweak( -unsatisfied ) { | 
| 588 | 5 |  |  | 5 | 1 | 20 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 589 |  |  |  |  |  |  | $self->_frame_push( | 
| 590 | 5 | 50 |  |  |  | 76 | begin => @args ? \@args : $self->{frame}[-1]{args}); | 
| 591 | 5 |  |  |  |  | 21 | $self->{frame}[-1]{level1} = $opt->{level1}; | 
| 592 | 5 |  |  |  |  | 23 | return; | 
| 593 | 20 |  |  | 20 |  | 8574 | } | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 110 |  | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | # -level1 is UNSUPPORTED and may be removed without warning. It is only | 
| 596 |  |  |  |  |  |  | # there for me to screw around with. | 
| 597 |  |  |  |  |  |  | BEGIN { | 
| 598 |  |  |  |  |  |  | $ENV{SATPASS2_LEVEL1} | 
| 599 | 20 | 50 |  | 20 |  | 8288 | and __PACKAGE__->MODIFY_CODE_ATTRIBUTES( | 
| 600 |  |  |  |  |  |  | \&begin, | 
| 601 |  |  |  |  |  |  | 'Verb( level1! )', | 
| 602 |  |  |  |  |  |  | ); | 
| 603 |  |  |  |  |  |  | } | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | sub cd : Verb() { | 
| 606 | 2 |  |  | 2 | 1 | 102 | my ( $self, undef, $dir ) = __arguments( @_ );	# $opt unused | 
| 607 | 2 | 100 |  |  |  | 40 | if (defined($dir)) { | 
| 608 | 1 | 50 |  |  |  | 56 | chdir $dir or $self->wail("Can not cd to $dir: $!"); | 
| 609 |  |  |  |  |  |  | } else { | 
| 610 | 1 | 50 |  |  |  | 32 | chdir File::HomeDir->my_home() | 
| 611 |  |  |  |  |  |  | or $self->wail("Can not cd to home: $!"); | 
| 612 |  |  |  |  |  |  | } | 
| 613 | 2 |  |  |  |  | 75 | return; | 
| 614 | 20 |  |  | 20 |  | 167 | } | 
|  | 20 |  |  |  |  | 54 |  | 
|  | 20 |  |  |  |  | 180 |  | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | sub choose : Verb( epoch=s ) { | 
| 617 | 2 |  |  | 2 | 1 | 13 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 618 |  |  |  |  |  |  |  | 
| 619 | 2 | 50 |  |  |  | 13 | if ($opt->{epoch}) { | 
| 620 | 0 |  |  |  |  | 0 | my $epoch = $self->__parse_time($opt->{epoch}); | 
| 621 |  |  |  |  |  |  | $self->{bodies} = [ | 
| 622 |  |  |  |  |  |  | map { | 
| 623 | 0 |  |  |  |  | 0 | $_->select($epoch); | 
| 624 |  |  |  |  |  |  | } | 
| 625 |  |  |  |  |  |  | $self->_aggregate( $self->{bodies} ) | 
| 626 | 0 |  |  |  |  | 0 | ]; | 
| 627 |  |  |  |  |  |  | } | 
| 628 | 2 | 50 |  |  |  | 10 | if ( @args ) { | 
| 629 | 2 | 50 |  |  |  | 4 | my @bodies = @{ $self->__choose( \@args, $self->{bodies} ) } | 
|  | 2 |  |  |  |  | 12 |  | 
| 630 |  |  |  |  |  |  | or return $self->__wail( 'No bodies chosen' ); | 
| 631 | 2 |  |  |  |  | 8 | @{ $self->{bodies} } = @bodies; | 
|  | 2 |  |  |  |  | 15 |  | 
| 632 |  |  |  |  |  |  | } | 
| 633 | 2 |  |  |  |  | 8 | return; | 
| 634 | 20 |  |  | 20 |  | 7472 | } | 
|  | 20 |  |  |  |  | 66 |  | 
|  | 20 |  |  |  |  | 159 |  | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | sub clear : Verb() { | 
| 637 | 5 |  |  | 5 | 1 | 49 | my ( $self ) = __arguments( @_ );	# $opt, @args unused | 
| 638 | 5 |  |  |  |  | 26 | @{$self->{bodies}} = (); | 
|  | 5 |  |  |  |  | 139 |  | 
| 639 | 5 |  |  |  |  | 32 | return; | 
| 640 | 20 |  |  | 20 |  | 5315 | } | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 116 |  | 
| 641 |  |  |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | sub dispatch { | 
| 643 | 289 |  |  | 289 | 1 | 977 | my ($self, $verb, @args) = @_; | 
| 644 |  |  |  |  |  |  |  | 
| 645 | 289 | 50 |  |  |  | 745 | defined $verb or return; | 
| 646 |  |  |  |  |  |  |  | 
| 647 | 289 |  |  |  |  | 893 | my $unsatisfied = $self->_in_unsatisfied_if(); | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 289 | 100 |  |  |  | 850 | if ( $self->{macro}{$verb} ) { | 
| 650 | 19 | 50 |  |  |  | 49 | $unsatisfied | 
| 651 |  |  |  |  |  |  | and return; | 
| 652 | 19 |  |  |  |  | 60 | return $self->_macro( $verb, @args ); | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 | 270 |  |  |  |  | 460 | my $code; | 
| 656 | 270 |  |  |  |  | 512 | $verb =~ s/ \A core [.] //smx; | 
| 657 | 270 | 100 | 66 |  |  | 1321 | $code = $self->can($verb) | 
| 658 |  |  |  |  |  |  | and $self->__get_attr($code, 'Verb') | 
| 659 |  |  |  |  |  |  | or $self->wail("Unknown interactive method '$verb'"); | 
| 660 |  |  |  |  |  |  |  | 
| 661 | 269 |  |  |  |  | 539 | my $rslt; | 
| 662 |  |  |  |  |  |  | $unsatisfied | 
| 663 |  |  |  |  |  |  | and not $self->__get_attr( $code, Tweak => {} )->{unsatisfied} | 
| 664 | 269 | 100 | 100 |  |  | 1374 | or $rslt = $code->( $self, @args ); | 
| 665 |  |  |  |  |  |  |  | 
| 666 | 261 | 100 |  |  |  | 30554 | defined $rslt | 
| 667 |  |  |  |  |  |  | and $rslt =~ s/ (? | 
| 668 |  |  |  |  |  |  |  | 
| 669 | 261 |  |  |  |  | 507 | foreach my $code ( | 
| 670 | 261 | 100 |  |  |  | 1522 | reverse @{ delete( $self->{frame}[-1]{post_dispatch} ) || [] } | 
| 671 |  |  |  |  |  |  | ) { | 
| 672 | 23 |  |  |  |  | 48 | my $append; | 
| 673 | 23 | 100 |  |  |  | 59 | defined( $append = $code->( $self ) ) | 
| 674 |  |  |  |  |  |  | and $rslt .= $append; | 
| 675 |  |  |  |  |  |  | } | 
| 676 | 261 |  |  |  |  | 1156 | return $rslt; | 
| 677 |  |  |  |  |  |  | } | 
| 678 |  |  |  |  |  |  |  | 
| 679 |  |  |  |  |  |  | { | 
| 680 |  |  |  |  |  |  | my %special = ( | 
| 681 |  |  |  |  |  |  | begin	=> sub { | 
| 682 |  |  |  |  |  |  | my ( $self, $verb ) = @_; | 
| 683 |  |  |  |  |  |  | $self->_is_interactive() | 
| 684 |  |  |  |  |  |  | or $self->wail( | 
| 685 |  |  |  |  |  |  | "'begin' forbidden in non-interactive $verb()" ); | 
| 686 |  |  |  |  |  |  | return; | 
| 687 |  |  |  |  |  |  | }, | 
| 688 |  |  |  |  |  |  | end	=> sub { | 
| 689 |  |  |  |  |  |  | my ( $self, $verb ) = @_; | 
| 690 |  |  |  |  |  |  | $self->wail( "'end' forbidden in $verb()" ); | 
| 691 |  |  |  |  |  |  | }, | 
| 692 |  |  |  |  |  |  | ); | 
| 693 |  |  |  |  |  |  |  | 
| 694 |  |  |  |  |  |  | sub _dispatch_check { | 
| 695 | 23 |  |  | 23 |  | 68 | my ( $self, $verb, $disp ) = @_; | 
| 696 | 23 | 100 |  |  |  | 79 | my $code = $special{$disp} | 
| 697 |  |  |  |  |  |  | or return; | 
| 698 | 4 |  |  |  |  | 32 | return $code->( $self, $verb, $disp ); | 
| 699 |  |  |  |  |  |  | } | 
| 700 |  |  |  |  |  |  | } | 
| 701 |  |  |  |  |  |  |  | 
| 702 |  |  |  |  |  |  | sub drop : Verb() { | 
| 703 | 1 |  |  | 1 | 1 | 5 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 704 |  |  |  |  |  |  |  | 
| 705 |  |  |  |  |  |  | @args | 
| 706 | 1 | 50 |  |  |  | 16 | or return; | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | my @bodies = @{ | 
| 709 | 1 | 50 |  |  |  | 3 | $self->__choose( { invert => 1 }, \@args, $self->{bodies} ) } | 
|  | 1 |  |  |  |  | 17 |  | 
| 710 |  |  |  |  |  |  | or return $self->__wail( 'No bodies left' ); | 
| 711 |  |  |  |  |  |  |  | 
| 712 | 1 |  |  |  |  | 4 | @{ $self->{bodies} } = @bodies; | 
|  | 1 |  |  |  |  | 4 |  | 
| 713 |  |  |  |  |  |  |  | 
| 714 | 1 |  |  |  |  | 9 | return; | 
| 715 | 20 |  |  | 20 |  | 15446 | } | 
|  | 20 |  |  |  |  | 65 |  | 
|  | 20 |  |  |  |  | 113 |  | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | sub dump : method Verb() Tweak( -unsatisfied ) {	## no critic (ProhibitBuiltInHomonyms) | 
| 718 | 0 |  |  | 0 | 1 | 0 | my ( $self, undef, @arg ) = __arguments( @_ );	# $opt unused | 
| 719 |  |  |  |  |  |  |  | 
| 720 | 0 |  |  |  |  | 0 | local $self->{time_parser} = ref $self->{time_parser}; | 
| 721 |  |  |  |  |  |  |  | 
| 722 | 0 |  |  |  |  | 0 | my $dumper = $self->_get_dumper(); | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | @arg | 
| 725 | 0 | 0 |  |  |  | 0 | or return $dumper->( $self ); | 
| 726 |  |  |  |  |  |  |  | 
| 727 | 0 |  |  |  |  | 0 | local $_ = shift @arg; | 
| 728 |  |  |  |  |  |  |  | 
| 729 |  |  |  |  |  |  | ref | 
| 730 | 0 | 0 |  |  |  | 0 | and return $dumper->( $_ ); | 
| 731 |  |  |  |  |  |  |  | 
| 732 |  |  |  |  |  |  | m/ \A frames? \z /smxi | 
| 733 | 0 | 0 |  |  |  | 0 | and return $dumper->( $self->{frame} ); | 
| 734 |  |  |  |  |  |  |  | 
| 735 | 0 | 0 |  |  |  | 0 | m/ \A tokens? \z /smxi | 
| 736 |  |  |  |  |  |  | and return $dumper->( $self->__tokenize( @arg ) ); | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | m/ \A twilight \z /smxi | 
| 739 |  |  |  |  |  |  | and return $dumper->( | 
| 740 | 0 | 0 |  |  |  | 0 | { map { $_ => $self->{$_} } qw{ twilight _twilight } } ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 741 |  |  |  |  |  |  |  | 
| 742 | 0 |  |  |  |  | 0 | my @stuff = $self->__choose( [ $_ ], $self->{bodies} ); | 
| 743 | 0 | 0 |  |  |  | 0 | if ( defined( my $inx = $self->_find_in_sky( $_ ) ) ) { | 
| 744 | 0 |  |  |  |  | 0 | push @stuff, $self->{sky}[$inx]; | 
| 745 |  |  |  |  |  |  | } | 
| 746 |  |  |  |  |  |  | @stuff | 
| 747 | 0 | 0 |  |  |  | 0 | and return $dumper->( @stuff ); | 
| 748 |  |  |  |  |  |  |  | 
| 749 | 0 |  |  |  |  | 0 | $self->whinge( "Dump argument '$_' not recognized" ); | 
| 750 |  |  |  |  |  |  |  | 
| 751 | 0 |  |  |  |  | 0 | return; | 
| 752 | 20 |  |  | 20 |  | 10083 | } | 
|  | 20 |  |  |  |  | 58 |  | 
|  | 20 |  |  |  |  | 109 |  | 
| 753 |  |  |  |  |  |  |  | 
| 754 |  |  |  |  |  |  | sub echo : Verb( n! ) { | 
| 755 | 44 |  |  | 44 | 1 | 170 | my ( undef, $opt, @args ) = __arguments( @_ );	# Invocant unused | 
| 756 | 44 |  |  |  |  | 194 | my $output = join( ' ', @args ); | 
| 757 | 44 | 50 |  |  |  | 132 | $opt->{n} or $output .= "\n"; | 
| 758 | 44 |  |  |  |  | 153 | return $output; | 
| 759 | 20 |  |  | 20 |  | 6273 | } | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 114 |  | 
| 760 |  |  |  |  |  |  |  | 
| 761 |  |  |  |  |  |  | sub else : method Verb() Tweak( -unsatisfied ) {	## no critic (ProhibitBuiltInHomonyms) | 
| 762 | 2 |  |  | 2 | 1 | 8 | my ( $self ) = __arguments( @_ );	# $opt, @args unused | 
| 763 |  |  |  |  |  |  |  | 
| 764 | 2 |  |  |  |  | 23 | @{ $self->{frame} } > 1 | 
| 765 |  |  |  |  |  |  | and 'begin' eq $self->{frame}[-1]{type} | 
| 766 |  |  |  |  |  |  | and 'if' eq $self->{frame}[-2]{type} | 
| 767 | 2 | 50 | 33 |  |  | 5 | or $self->wail( 'Else without if ... then begin' ); | 
|  |  |  | 33 |  |  |  |  | 
| 768 |  |  |  |  |  |  |  | 
| 769 | 2 | 50 |  |  |  | 19 | $self->{frame}[-1]{in_else}++ | 
| 770 |  |  |  |  |  |  | and $self->wail( 'Only one else may follow an if' ); | 
| 771 |  |  |  |  |  |  |  | 
| 772 | 2 |  |  |  |  | 15 | return $self->_twiddle_condition( ! $self->{frame}[-2]{condition} ); | 
| 773 | 20 |  |  | 20 |  | 6738 | } | 
|  | 20 |  |  |  |  | 58 |  | 
|  | 20 |  |  |  |  | 145 |  | 
| 774 |  |  |  |  |  |  |  | 
| 775 |  |  |  |  |  |  | sub _twiddle_condition { | 
| 776 | 4 |  |  | 4 |  | 11 | my ( $self, $cond ) = @_; | 
| 777 |  |  |  |  |  |  |  | 
| 778 |  |  |  |  |  |  | # Here is where I pay for the convenience of the if() | 
| 779 |  |  |  |  |  |  | # implementation. The if() itself is a frame because I do not yet | 
| 780 |  |  |  |  |  |  | # know if it will entail a begin(). But I can't do an else() unless | 
| 781 |  |  |  |  |  |  | # there is in fact a begin(), which creates another frame. So I end | 
| 782 |  |  |  |  |  |  | # up twiddling values in both frames. | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | $self->{frame}[-1]{unsatisfied_if} = | 
| 785 |  |  |  |  |  |  | $self->{frame}[-2]{unsatisfied_if} = | 
| 786 |  |  |  |  |  |  | ! $cond || ( | 
| 787 |  |  |  |  |  |  | @{ $self->{frame} } > 2 ? | 
| 788 |  |  |  |  |  |  | $self->{frame}[-3]{unsatisfied_if} : | 
| 789 | 4 |  | 66 |  |  | 15 | 0 | 
| 790 |  |  |  |  |  |  | ); | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | $self->{frame}[-1]{condition} = | 
| 793 | 4 |  |  |  |  | 12 | $self->{frame}[-2]{condition} = $cond; | 
| 794 |  |  |  |  |  |  |  | 
| 795 | 4 |  |  |  |  | 15 | return; | 
| 796 |  |  |  |  |  |  | } | 
| 797 |  |  |  |  |  |  |  | 
| 798 |  |  |  |  |  |  | sub end : Verb() Tweak( -unsatisfied ) { | 
| 799 | 5 |  |  | 5 | 1 | 45 | my ( $self ) = __arguments( @_ );	# $opt, @args unused | 
| 800 |  |  |  |  |  |  |  | 
| 801 | 5 | 50 |  |  |  | 43 | $self->{frame}[-1]{type} eq 'begin' | 
| 802 |  |  |  |  |  |  | or $self->wail( 'End without begin' ); | 
| 803 | 5 |  |  |  |  | 45 | $self->_frame_pop(); | 
| 804 | 5 |  |  |  |  | 12 | return; | 
| 805 | 20 |  |  | 20 |  | 8998 | } | 
|  | 20 |  |  |  |  | 61 |  | 
|  | 20 |  |  |  |  | 113 |  | 
| 806 |  |  |  |  |  |  |  | 
| 807 |  |  |  |  |  |  | sub error : Verb() { | 
| 808 | 1 |  |  | 1 | 1 | 6 | my ( $self, undef, @arg ) = __arguments( @_ ); | 
| 809 |  |  |  |  |  |  | @arg | 
| 810 | 1 | 50 |  |  |  | 11 | or push @arg, 'An error has occurred'; | 
| 811 | 1 |  |  |  |  | 8 | $self->wail( @arg ); | 
| 812 | 0 |  |  |  |  | 0 | return; | 
| 813 | 20 |  |  | 20 |  | 6087 | } | 
|  | 20 |  |  |  |  | 68 |  | 
|  | 20 |  |  |  |  | 156 |  | 
| 814 |  |  |  |  |  |  |  | 
| 815 |  |  |  |  |  |  | # Tokenize and execute one or more commands. Optionally (and | 
| 816 |  |  |  |  |  |  | # unsupportedly) you can pass a code reference as the first argument. | 
| 817 |  |  |  |  |  |  | # This code reference will be used to fetch commands when the arguments | 
| 818 |  |  |  |  |  |  | # are exhausted. IF you pass your own code reference, we return after | 
| 819 |  |  |  |  |  |  | # the first command, since the code reference is presumed to manage the | 
| 820 |  |  |  |  |  |  | # input stream itself. | 
| 821 |  |  |  |  |  |  | sub execute { | 
| 822 | 264 |  |  | 264 | 1 | 723 | my ($self, @args) = @_; | 
| 823 | 264 |  |  |  |  | 642 | my $accum; | 
| 824 |  |  |  |  |  |  | my $in; | 
| 825 | 264 |  |  |  |  | 0 | my $extern; | 
| 826 | 264 | 100 |  |  |  | 800 | if ( CODE_REF eq ref $args[0] ) { | 
| 827 | 13 |  |  |  |  | 36 | $extern = shift @args; | 
| 828 |  |  |  |  |  |  | $in = sub { | 
| 829 | 21 |  |  | 21 |  | 49 | my ( $prompt ) = @_; | 
| 830 | 21 | 100 |  |  |  | 83 | @args and return shift @args; | 
| 831 | 8 |  |  |  |  | 36 | return $extern->( $prompt ); | 
| 832 | 13 |  |  |  |  | 76 | }; | 
| 833 |  |  |  |  |  |  | } else { | 
| 834 | 251 |  |  | 502 |  | 1156 | $in = sub { return shift @args }; | 
|  | 502 |  |  |  |  | 1700 |  | 
| 835 |  |  |  |  |  |  | } | 
| 836 | 264 |  |  |  |  | 618 | @args = map { split qr{ (?<= \n ) }smx, $_ } @args; | 
|  | 265 |  |  |  |  | 3342 |  | 
| 837 | 264 |  |  |  |  | 912 | while ( defined ( local $_ = $in->( $self->get( 'prompt' ) ) ) ) { | 
| 838 | 280 | 50 |  |  |  | 753 | $self->{echo} and $self->whinge($self->get( 'prompt' ), $_); | 
| 839 | 280 | 100 |  |  |  | 938 | m/ \A \s* [#] /smx and next; | 
| 840 | 277 |  |  |  |  | 639 | my $stdout = $self->{frame}[-1]{stdout}; | 
| 841 |  |  |  |  |  |  | my ($args, $redirect) = $self->__tokenize( | 
| 842 | 277 |  |  |  |  | 1391 | { in => $in }, $_, $self->{frame}[-1]{args}); | 
| 843 |  |  |  |  |  |  | # NOTICE | 
| 844 |  |  |  |  |  |  | # | 
| 845 |  |  |  |  |  |  | # The execute_filter attribute is undocumented and unsupported. | 
| 846 |  |  |  |  |  |  | # It exists only so I can scavenge the user's initialization | 
| 847 |  |  |  |  |  |  | # file for the (possible) Space Track username and password, to | 
| 848 |  |  |  |  |  |  | # be used in testing, without being subject to any other | 
| 849 |  |  |  |  |  |  | # undesired side effects, such as running a prediction and | 
| 850 |  |  |  |  |  |  | # exiting. If I change my mind on how or whether to do this, | 
| 851 |  |  |  |  |  |  | # execute_filter will be altered or retracted without warning, | 
| 852 |  |  |  |  |  |  | # much less a deprecation cycle. If you have a legitimate need | 
| 853 |  |  |  |  |  |  | # for this functionality, contact me. | 
| 854 |  |  |  |  |  |  | # | 
| 855 |  |  |  |  |  |  | # YOU HAVE BEEN WARNED. | 
| 856 | 273 | 100 |  |  |  | 1132 | $self->{execute_filter}->( $self, $args ) or next; | 
| 857 | 267 | 100 |  |  |  | 1159 | @{ $args } or next; | 
|  | 267 |  |  |  |  | 658 |  | 
| 858 | 266 | 100 |  |  |  | 596 | if ($redirect->{'>'}) { | 
| 859 | 1 |  |  |  |  | 15 | my ( $mode, $name ) = map { $redirect->{'>'}{$_} } qw{ mode name }; | 
|  | 2 |  |  |  |  | 7 |  | 
| 860 | 1 |  |  |  |  | 13 | my $fh; | 
| 861 |  |  |  |  |  |  | $stdout = sub { | 
| 862 | 1 |  |  | 1 |  | 3 | my ( $output ) = @_; | 
| 863 | 1 |  | 33 |  |  | 8 | $fh ||= $self->_file_opener( $name, $mode ); | 
| 864 | 1 |  |  |  |  | 12 | $fh->print( $output ); | 
| 865 | 1 |  |  |  |  | 13 | return; | 
| 866 | 1 |  |  |  |  | 10 | }; | 
| 867 |  |  |  |  |  |  | } | 
| 868 |  |  |  |  |  |  |  | 
| 869 |  |  |  |  |  |  | # {localout} is the output to be used for this command. It goes | 
| 870 |  |  |  |  |  |  | # in the frame stack because our command may start a new frame, | 
| 871 |  |  |  |  |  |  | # and _frame_push() needs to have a place to get the correct | 
| 872 |  |  |  |  |  |  | # output handle. | 
| 873 |  |  |  |  |  |  |  | 
| 874 | 266 |  |  |  |  | 382 | my $frame_depth = $#{$self->{frame}}; | 
|  | 266 |  |  |  |  | 624 |  | 
| 875 | 266 |  |  |  |  | 710 | $self->{frame}[-1]{localout} = $stdout; | 
| 876 |  |  |  |  |  |  |  | 
| 877 | 266 |  |  |  |  | 784 | my $output = $self->dispatch( @$args ); | 
| 878 |  |  |  |  |  |  |  | 
| 879 | 256 |  |  |  |  | 1001 | $#{$self->{frame}} >= $frame_depth | 
| 880 | 256 | 100 |  |  |  | 437 | and delete $self->{frame}[ $frame_depth ]{localout}; | 
| 881 |  |  |  |  |  |  |  | 
| 882 | 256 | 100 |  |  |  | 1261 | $self->_execute_output( $output, | 
| 883 |  |  |  |  |  |  | defined $stdout ? $stdout : \$accum ); | 
| 884 |  |  |  |  |  |  |  | 
| 885 | 256 | 100 |  |  |  | 1589 | $extern and last; | 
| 886 |  |  |  |  |  |  | } | 
| 887 | 250 |  |  |  |  | 1602 | return $accum; | 
| 888 |  |  |  |  |  |  | } | 
| 889 |  |  |  |  |  |  |  | 
| 890 |  |  |  |  |  |  | #	$satpass2->_execute(...); | 
| 891 |  |  |  |  |  |  | # | 
| 892 |  |  |  |  |  |  | #	This subroutine calls $satpass2->execute() once for each | 
| 893 |  |  |  |  |  |  | #	argument. The call is wrapped in an eval{}; if an exception | 
| 894 |  |  |  |  |  |  | #	occurs the user is notified via warn. | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | sub _execute { | 
| 897 | 0 |  |  | 0 |  | 0 | my ($self, @args) = @_; | 
| 898 |  |  |  |  |  |  | my $in = CODE_REF eq ref $args[0] ? shift @args : sub { return shift | 
| 899 | 0 | 0 |  | 0 |  | 0 | @args }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 900 | 0 |  |  |  |  | 0 | while ( @args ) { | 
| 901 | 0 |  |  | 0 |  | 0 | local $SIG{INT} = sub {die "\n$interrupted\n"}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 902 | 0 | 0 |  |  |  | 0 | eval { | 
| 903 | 0 |  |  |  |  | 0 | $self->execute( $in, shift @args ); | 
| 904 | 0 |  |  |  |  | 0 | 1; | 
| 905 |  |  |  |  |  |  | } or warn $@;	# Not whinge, since presumably we already did. | 
| 906 |  |  |  |  |  |  | } | 
| 907 | 0 |  |  |  |  | 0 | return; | 
| 908 |  |  |  |  |  |  | } | 
| 909 |  |  |  |  |  |  |  | 
| 910 |  |  |  |  |  |  | #	$satpass2->_execute_output( $output, $stdout ); | 
| 911 |  |  |  |  |  |  | # | 
| 912 |  |  |  |  |  |  | #	If $output is defined, sends it to $stdout. | 
| 913 |  |  |  |  |  |  |  | 
| 914 |  |  |  |  |  |  | sub _execute_output { | 
| 915 | 256 |  |  | 256 |  | 699 | my ( undef, $output, $stdout ) = @_;	# Invocant unused | 
| 916 | 256 | 100 |  |  |  | 643 | defined $output or return; | 
| 917 | 152 |  |  |  |  | 387 | my $ref = ref $stdout; | 
| 918 | 152 | 50 |  |  |  | 575 | if ( !defined $stdout ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 919 | 0 |  |  |  |  | 0 | return $output; | 
| 920 |  |  |  |  |  |  | } elsif ( SCALAR_REF eq $ref ) { | 
| 921 | 149 |  |  |  |  | 465 | $$stdout .= $output; | 
| 922 |  |  |  |  |  |  | } elsif ( CODE_REF eq $ref ) { | 
| 923 | 2 |  |  |  |  | 7 | $stdout->( $output ); | 
| 924 |  |  |  |  |  |  | } elsif ( ARRAY_REF eq $ref ) { | 
| 925 | 1 |  |  |  |  | 18 | push @$stdout, split qr{ (?<=\n) }smx, $output; | 
| 926 |  |  |  |  |  |  | } else { | 
| 927 | 0 |  |  |  |  | 0 | $stdout->print( $output ); | 
| 928 |  |  |  |  |  |  | } | 
| 929 | 152 |  |  |  |  | 280 | return; | 
| 930 |  |  |  |  |  |  | } | 
| 931 |  |  |  |  |  |  |  | 
| 932 |  |  |  |  |  |  | sub exit : method Verb() {	## no critic (ProhibitBuiltInHomonyms) | 
| 933 | 1 |  |  | 1 | 1 | 8 | my ( $self ) = __arguments( @_ );	# $opt, @args unused | 
| 934 |  |  |  |  |  |  |  | 
| 935 | 1 |  |  |  |  | 26 | $self->_frame_pop(1);	# Leave only the inital frame. | 
| 936 |  |  |  |  |  |  |  | 
| 937 | 1 |  |  |  |  | 3 | eval {	## no critic (RequireCheckingReturnValueOfEval) | 
| 938 | 20 |  |  | 20 |  | 23573 | no warnings qw{exiting}; | 
|  | 20 |  |  |  |  | 88 |  | 
|  | 20 |  |  |  |  | 2005 |  | 
| 939 | 1 |  |  |  |  | 13 | last SATPASS2_EXECUTE; | 
| 940 |  |  |  |  |  |  | }; | 
| 941 | 0 |  |  |  |  | 0 | $self->whinge("$@Exiting Perl"); | 
| 942 | 0 |  |  |  |  | 0 | exit; | 
| 943 |  |  |  |  |  |  |  | 
| 944 | 20 |  |  | 20 |  | 159 | } | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 127 |  | 
| 945 |  |  |  |  |  |  |  | 
| 946 |  |  |  |  |  |  | sub export : Verb() { | 
| 947 | 4 |  |  | 4 | 1 | 23 | my ( $self, undef, $name, @args ) = __arguments( @_ );	# $opt unused | 
| 948 | 4 | 100 |  |  |  | 39 | if ($mutator{$name}) { | 
| 949 | 1 | 50 |  |  |  | 13 | @args and $self->set ($name, shift @args); | 
| 950 | 1 |  |  |  |  | 5 | $self->{exported}{$name} = 1; | 
| 951 |  |  |  |  |  |  | } else { | 
| 952 | 3 | 100 |  |  |  | 61 | @args or return $self->wail( 'You must specify a value' ); | 
| 953 | 2 |  |  |  |  | 31 | $self->{exported}{$name} = shift @args; | 
| 954 |  |  |  |  |  |  | } | 
| 955 | 3 |  |  |  |  | 12 | return; | 
| 956 | 20 |  |  | 20 |  | 6443 | } | 
|  | 20 |  |  |  |  | 57 |  | 
|  | 20 |  |  |  |  | 132 |  | 
| 957 |  |  |  |  |  |  |  | 
| 958 |  |  |  |  |  |  | # Attributes must all be on one line to process correctly under Perl | 
| 959 |  |  |  |  |  |  | # 5.8.8. | 
| 960 |  |  |  |  |  |  | sub flare : Verb( algorithm=s am! choose=s@ day! dump! pm! questionable|spare! quiet! tz|zone=s ) | 
| 961 |  |  |  |  |  |  | { | 
| 962 | 0 |  |  | 0 | 1 | 0 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 963 | 0 |  |  |  |  | 0 | HAVE_TLE_IRIDIUM | 
| 964 |  |  |  |  |  |  | or $self->wail( 'Astro::Coord::ECI::TLE::Iridium not available' ); | 
| 965 | 0 |  |  |  |  | 0 | my $pass_start = $self->__parse_time ( | 
| 966 |  |  |  |  |  |  | shift @args, $self->_get_day_noon()); | 
| 967 | 0 |  | 0 |  |  | 0 | my $pass_end = $self->__parse_time (shift @args || '+7'); | 
| 968 | 0 | 0 |  |  |  | 0 | $pass_start >= $pass_end | 
| 969 |  |  |  |  |  |  | and $self->wail( 'End time must be after start time' ); | 
| 970 | 0 |  |  |  |  | 0 | my $sta = $self->station(); | 
| 971 |  |  |  |  |  |  |  | 
| 972 | 0 |  |  |  |  | 0 | my $max_mirror_angle = deg2rad( $self->{max_mirror_angle} ); | 
| 973 | 0 |  |  |  |  | 0 | my $horizon = deg2rad ($self->{horizon}); | 
| 974 | 0 |  |  |  |  | 0 | my $twilight = $self->{_twilight}; | 
| 975 | 0 |  |  |  |  | 0 | my @flare_mag = ($self->{flare_mag_night}, $self->{flare_mag_day}); | 
| 976 |  |  |  |  |  |  | my $zone = exists $opt->{tz} ? $opt->{tz} : | 
| 977 |  |  |  |  |  |  | $self->{formatter}->gmt() ? 0 : | 
| 978 | 0 | 0 | 0 |  |  | 0 | $self->{formatter}->tz() || undef; | 
|  |  | 0 |  |  |  |  |  | 
| 979 |  |  |  |  |  |  |  | 
| 980 | 0 |  |  |  |  | 0 | $self->_apply_boolean_default( | 
| 981 |  |  |  |  |  |  | $opt, 0, qw{ am day pm } ); | 
| 982 |  |  |  |  |  |  |  | 
| 983 |  |  |  |  |  |  | #	Decide which model to use. | 
| 984 |  |  |  |  |  |  |  | 
| 985 | 0 |  |  |  |  | 0 | my $model = $self->{model}; | 
| 986 |  |  |  |  |  |  |  | 
| 987 |  |  |  |  |  |  | #	Select only the bodies capable of flaring. | 
| 988 |  |  |  |  |  |  |  | 
| 989 | 0 |  |  |  |  | 0 | my @active; | 
| 990 | 0 |  |  |  |  | 0 | foreach my $tle ( $self->_aggregate( | 
| 991 |  |  |  |  |  |  | scalar $self->__choose( $opt->{choose}, $self->{bodies} ) | 
| 992 |  |  |  |  |  |  | ) ) | 
| 993 |  |  |  |  |  |  | { | 
| 994 | 0 | 0 |  |  |  | 0 | $tle->can_flare( $opt->{questionable} ) or next; | 
| 995 |  |  |  |  |  |  | $tle->set ( | 
| 996 |  |  |  |  |  |  | algorithm	=> $opt->{algorithm} || 'fixed', | 
| 997 |  |  |  |  |  |  | backdate	=> $self->{backdate}, | 
| 998 |  |  |  |  |  |  | edge_of_earths_shadow => $self->{edge_of_earths_shadow}, | 
| 999 |  |  |  |  |  |  | horizon	=> $horizon, | 
| 1000 |  |  |  |  |  |  | twilight	=> $twilight, | 
| 1001 |  |  |  |  |  |  | model	=> $model, | 
| 1002 |  |  |  |  |  |  | am		=> $opt->{am}, | 
| 1003 |  |  |  |  |  |  | max_mirror_angle => $max_mirror_angle, | 
| 1004 |  |  |  |  |  |  | day		=> $opt->{day}, | 
| 1005 |  |  |  |  |  |  | pm		=> $opt->{pm}, | 
| 1006 |  |  |  |  |  |  | extinction	=> $self->{extinction}, | 
| 1007 | 0 |  | 0 |  |  | 0 | station	=> $sta, | 
| 1008 |  |  |  |  |  |  | zone	=> $zone, | 
| 1009 |  |  |  |  |  |  | ); | 
| 1010 | 0 |  |  |  |  | 0 | push @active, $tle; | 
| 1011 |  |  |  |  |  |  | } | 
| 1012 | 0 | 0 |  |  |  | 0 | @active or return $self->__wail( 'No bodies capable of flaring' ); | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 | 0 |  |  |  |  | 0 | my @flares; | 
| 1015 | 0 |  |  |  |  | 0 | foreach my $tle (@active) { | 
| 1016 |  |  |  |  |  |  | eval { | 
| 1017 | 0 |  |  |  |  | 0 | push @flares, $tle->flare( $pass_start, $pass_end ); | 
| 1018 | 0 |  |  |  |  | 0 | 1; | 
| 1019 | 0 | 0 |  |  |  | 0 | } or do { | 
| 1020 | 0 | 0 |  |  |  | 0 | $@ =~ m/ \Q$interrupted\E /smxo and $self->wail($@); | 
| 1021 | 0 | 0 |  |  |  | 0 | $opt->{quiet} or $self->whinge($@); | 
| 1022 |  |  |  |  |  |  | }; | 
| 1023 |  |  |  |  |  |  | } | 
| 1024 |  |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | # Record number of events found | 
| 1026 |  |  |  |  |  |  |  | 
| 1027 | 0 |  |  |  |  | 0 | @flares = sort { $a->{time} <=> $b->{time} } | 
| 1028 | 0 |  |  |  |  | 0 | grep { $_->{magnitude} <= $flare_mag[ | 
| 1029 | 0 | 0 |  |  |  | 0 | ( $_->{type} eq 'day' ? 1 : 0 ) ] } | 
| 1030 |  |  |  |  |  |  | @flares; | 
| 1031 | 0 |  |  |  |  | 0 | $self->{events} += @flares; | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 | 0 |  |  |  |  | 0 | return $self->__format_data( flare => \@flares, $opt ); | 
| 1034 | 20 |  |  | 20 |  | 14074 | } | 
|  | 20 |  |  |  |  | 49 |  | 
|  | 20 |  |  |  |  | 131 |  | 
| 1035 |  |  |  |  |  |  |  | 
| 1036 |  |  |  |  |  |  | sub formatter : Verb() Tweak( -completion _readline_complete_subcommand ) { | 
| 1037 | 9 | 50 |  | 9 | 1 | 48 | splice @_, ( HASH_REF eq ref $_[1] ? 2 : 1 ), 0, 'formatter'; | 
| 1038 | 9 |  |  |  |  | 47 | goto &_helper_handler; | 
| 1039 | 20 |  |  | 20 |  | 5117 | } | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 113 |  | 
| 1040 |  |  |  |  |  |  |  | 
| 1041 |  |  |  |  |  |  | # Calls to the following _formatter_sub method are generated dynamically | 
| 1042 |  |  |  |  |  |  | # above, so there is no way Perl::Critic can find them. | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 |  |  |  |  |  |  | sub _formatter_sub {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1045 | 0 |  |  | 0 |  | 0 | my ( $app, $text, $line, $start, @arg ) = @_; | 
| 1046 | 0 |  |  |  |  | 0 | my $fmtr = $app->get( 'formatter' ); | 
| 1047 | 0 | 0 |  |  |  | 0 | if ( @arg == 2 ) { | 
| 1048 | 0 |  |  |  |  | 0 | my @list = qw{ | 
| 1049 |  |  |  |  |  |  | date_format | 
| 1050 |  |  |  |  |  |  | desired_equinox_dynamical | 
| 1051 |  |  |  |  |  |  | gmt | 
| 1052 |  |  |  |  |  |  | local_coord | 
| 1053 |  |  |  |  |  |  | time_format | 
| 1054 |  |  |  |  |  |  | tz | 
| 1055 |  |  |  |  |  |  | }; | 
| 1056 | 0 | 0 |  |  |  | 0 | $fmtr->can( '__list_templates' ) | 
| 1057 |  |  |  |  |  |  | and push @list, 'template'; | 
| 1058 | 0 |  |  |  |  | 0 | my $re = qr/ \A \Q$arg[1]\E /smx; | 
| 1059 | 0 |  |  |  |  | 0 | return [ grep { $_ =~ $re } sort @list ]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1060 |  |  |  |  |  |  | } | 
| 1061 | 0 | 0 |  |  |  | 0 | my $code = $app->can( "_formatter_complete_$arg[1]" ) | 
| 1062 |  |  |  |  |  |  | or return; | 
| 1063 |  |  |  |  |  |  |  | 
| 1064 | 0 |  |  |  |  | 0 | my $r; | 
| 1065 | 0 | 0 |  |  |  | 0 | $r = $app->_readline_complete_options( $code, $text, $line, | 
| 1066 |  |  |  |  |  |  | $start ) | 
| 1067 |  |  |  |  |  |  | and return $r; | 
| 1068 |  |  |  |  |  |  |  | 
| 1069 | 0 |  |  |  |  | 0 | return $code->( $app, @arg ); | 
| 1070 |  |  |  |  |  |  | } | 
| 1071 |  |  |  |  |  |  |  | 
| 1072 |  |  |  |  |  |  | # Calls to the following _formatter_complete_... methods are generated | 
| 1073 |  |  |  |  |  |  | # dynamically above, so there is no way Perl::Critic can find them. | 
| 1074 |  |  |  |  |  |  | # The Verb attribute must aggree with _helper_handler(). | 
| 1075 |  |  |  |  |  |  |  | 
| 1076 |  |  |  |  |  |  | sub _formatter_complete_template : Verb( changes! raw! ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1077 | 0 |  |  | 0 |  | 0 | my ( $app, undef, @arg ) = __arguments( @_ ); | 
| 1078 | 0 |  |  |  |  | 0 | my $fmtr = $app->get( 'formatter' ); | 
| 1079 | 0 |  |  |  |  | 0 | my $re = qr/ \A \Q$arg[2]\E /smx; | 
| 1080 |  |  |  |  |  |  | return [ | 
| 1081 | 0 |  |  |  |  | 0 | grep { $_ =~ $re } | 
|  | 0 |  |  |  |  | 0 |  | 
| 1082 |  |  |  |  |  |  | sort( $fmtr->__list_templates() ) | 
| 1083 |  |  |  |  |  |  | ]; | 
| 1084 | 20 |  |  | 20 |  | 10995 | } | 
|  | 20 |  |  |  |  | 68 |  | 
|  | 20 |  |  |  |  | 133 |  | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | sub geocode : Verb( debug! ) { | 
| 1087 | 0 |  |  | 0 | 1 | 0 | my ( $self, $opt, $loc ) = __arguments( @_ ); | 
| 1088 |  |  |  |  |  |  |  | 
| 1089 | 0 |  |  |  |  | 0 | my $set_loc; | 
| 1090 | 0 | 0 |  |  |  | 0 | if ( defined $loc ) { | 
| 1091 | 0 |  |  |  |  | 0 | $set_loc = 1; | 
| 1092 |  |  |  |  |  |  | } else { | 
| 1093 | 0 |  |  |  |  | 0 | $loc = $self->get( 'location' ); | 
| 1094 |  |  |  |  |  |  | } | 
| 1095 |  |  |  |  |  |  |  | 
| 1096 | 0 |  |  |  |  | 0 | my $geocoder = $self->_helper_get_object( 'geocoder' ); | 
| 1097 |  |  |  |  |  |  |  | 
| 1098 | 0 |  |  |  |  | 0 | my @rslt = $geocoder->geocode( $loc ); | 
| 1099 |  |  |  |  |  |  |  | 
| 1100 | 0 |  |  |  |  | 0 | my $output; | 
| 1101 | 0 | 0 |  |  |  | 0 | if ( @rslt == 1 ) { | 
| 1102 |  |  |  |  |  |  | $set_loc | 
| 1103 | 0 | 0 |  |  |  | 0 | and $self->set( location => $rslt[0]{description} ); | 
| 1104 | 0 |  |  |  |  | 0 | $self->set( map { $_ => $rslt[0]{$_} } qw{ latitude | 
|  | 0 |  |  |  |  | 0 |  | 
| 1105 |  |  |  |  |  |  | longitude } ); | 
| 1106 | 0 | 0 |  |  |  | 0 | $output .= $self->show( | 
| 1107 |  |  |  |  |  |  | ( $set_loc ? 'location' : () ), qw{latitude longitude} ); | 
| 1108 | 0 | 0 |  |  |  | 0 | if ( $self->get( 'autoheight' ) ) { | 
| 1109 | 0 |  |  |  |  | 0 | $opt->{geocoding} = 1; | 
| 1110 | 0 |  |  |  |  | 0 | $output .= $self->_height_us($opt); | 
| 1111 |  |  |  |  |  |  | } | 
| 1112 |  |  |  |  |  |  | } else { | 
| 1113 | 0 |  |  |  |  | 0 | foreach my $poi ( @rslt ) { | 
| 1114 | 0 |  |  |  |  | 0 | $output .= join ' ', map { $poi->{$_} } qw{ latitude | 
|  | 0 |  |  |  |  | 0 |  | 
| 1115 |  |  |  |  |  |  | longitude description }; | 
| 1116 | 0 |  |  |  |  | 0 | $output =~ s/ (?: \A | (? | 
| 1117 |  |  |  |  |  |  | } | 
| 1118 |  |  |  |  |  |  | } | 
| 1119 | 0 |  |  |  |  | 0 | return $output; | 
| 1120 | 20 |  |  | 20 |  | 9650 | } | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 137 |  | 
| 1121 |  |  |  |  |  |  |  | 
| 1122 |  |  |  |  |  |  | sub geodetic : Verb() { | 
| 1123 | 0 |  |  | 0 | 1 | 0 | my ( $self, undef, $name, $lat, $lon, $alt ) = __arguments( @_ ); # $opt unused | 
| 1124 | 0 | 0 |  |  |  | 0 | @_ == 5 or $self->wail( 'Want exactly four arguments' ); | 
| 1125 | 0 |  |  |  |  | 0 | my $body = Astro::Coord::ECI::TLE->new( | 
| 1126 |  |  |  |  |  |  | name => $name, | 
| 1127 |  |  |  |  |  |  | id => '', | 
| 1128 |  |  |  |  |  |  | model => 'null', | 
| 1129 |  |  |  |  |  |  | )->geodetic( | 
| 1130 |  |  |  |  |  |  | deg2rad( $self->__parse_angle( $lat ) ), | 
| 1131 |  |  |  |  |  |  | deg2rad( $self->__parse_angle( $lon ) ), | 
| 1132 |  |  |  |  |  |  | $self->__parse_distance( $alt ), | 
| 1133 |  |  |  |  |  |  | ); | 
| 1134 | 0 |  |  |  |  | 0 | push @{ $self->{bodies} }, $body; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1135 | 0 |  |  |  |  | 0 | return; | 
| 1136 | 20 |  |  | 20 |  | 6614 | } | 
|  | 20 |  |  |  |  | 64 |  | 
|  | 20 |  |  |  |  | 113 |  | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 |  |  |  |  |  |  | sub get { | 
| 1139 | 927 |  |  | 927 | 1 | 2263 | my ($self, $name) = @_; | 
| 1140 | 927 |  |  |  |  | 2915 | $self->_attribute_exists( $name ); | 
| 1141 | 927 |  |  |  |  | 3194 | $self->_deprecation_notice( attribute => $name ); | 
| 1142 | 927 |  |  |  |  | 3388 | return $accessor{$name}->($self, $name); | 
| 1143 |  |  |  |  |  |  | } | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 |  |  |  |  |  |  | sub height : Verb( debug! ) { | 
| 1146 | 0 |  |  | 0 | 1 | 0 | return _height_us( __arguments( @_ ) ); | 
| 1147 | 20 |  |  | 20 |  | 6342 | } | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 145 |  | 
| 1148 |  |  |  |  |  |  |  | 
| 1149 |  |  |  |  |  |  | sub _height_us { | 
| 1150 | 0 |  |  | 0 |  | 0 | my ($self, $opt, @args) = @_; | 
| 1151 | 0 |  |  |  |  | 0 | $self->_load_module ('Geo::WebService::Elevation::USGS'); | 
| 1152 | 0 |  |  |  |  | 0 | my $eq = Geo::WebService::Elevation::USGS->new( | 
| 1153 |  |  |  |  |  |  | places => 2,	# Service returns unreasonable precision | 
| 1154 |  |  |  |  |  |  | units => 'METERS',	# default for service is 'FEET' | 
| 1155 |  |  |  |  |  |  | croak	=> 0,		# Handle our own errors | 
| 1156 |  |  |  |  |  |  | ); | 
| 1157 | 0 | 0 |  |  |  | 0 | @args or push @args, $self->get('latitude'), $self->get('longitude'); | 
| 1158 | 0 |  |  |  |  | 0 | my $output; | 
| 1159 | 0 |  |  |  |  | 0 | my ( $rslt ) = $eq->elevation(@args); | 
| 1160 | 0 | 0 |  |  |  | 0 | if ( $eq->is_valid( $rslt ) ) { | 
| 1161 | 0 |  |  |  |  | 0 | $self->set( height => $rslt->{Elevation} ); | 
| 1162 |  |  |  |  |  |  | } else { | 
| 1163 |  |  |  |  |  |  | $opt->{geocoding} | 
| 1164 | 0 | 0 | 0 |  |  | 0 | or $self->wail( $eq->error() || 'No valid result found' ); | 
| 1165 | 0 |  |  |  |  | 0 | $self->set( height => 0 ); | 
| 1166 | 0 |  |  |  |  | 0 | $output .= "# Unable to obtain height. Setting to 0\n"; | 
| 1167 |  |  |  |  |  |  | } | 
| 1168 | 0 |  |  |  |  | 0 | $output .= $self->show( 'height' ); | 
| 1169 | 0 |  |  |  |  | 0 | return $output; | 
| 1170 |  |  |  |  |  |  | } | 
| 1171 |  |  |  |  |  |  |  | 
| 1172 |  |  |  |  |  |  | sub help : Verb() { | 
| 1173 | 0 |  |  | 0 | 1 | 0 | my ( $self, undef, $arg ) = __arguments( @_ );	# $opt unused | 
| 1174 | 0 | 0 |  |  |  | 0 | defined $arg | 
| 1175 |  |  |  |  |  |  | or $arg = ''; | 
| 1176 |  |  |  |  |  |  | defined $self->{_help_module}{$arg} | 
| 1177 | 0 | 0 |  |  |  | 0 | and $arg = $self->{_help_module}{$arg}; | 
| 1178 | 0 | 0 |  |  |  | 0 | if ( my $cmd = $self->_get_browser_command() ) { | 
| 1179 | 0 | 0 |  |  |  | 0 | my $kind = $arg =~ m/ - /smx ? 'release' : 'pod'; | 
| 1180 | 0 |  |  |  |  | 0 | $self->system( $cmd, | 
| 1181 |  |  |  |  |  |  | "https://metacpan.org/$kind/$arg" ); | 
| 1182 |  |  |  |  |  |  | } else { | 
| 1183 |  |  |  |  |  |  |  | 
| 1184 | 0 |  |  |  |  | 0 | my $os_specific = "_help_$^O"; | 
| 1185 | 0 | 0 |  |  |  | 0 | if (__PACKAGE__->can ($os_specific)) { | 
|  |  | 0 |  |  |  |  |  | 
| 1186 | 0 |  |  |  |  | 0 | return __PACKAGE__->$os_specific (); | 
| 1187 |  |  |  |  |  |  | } elsif ( load_package( 'Pod::Usage' ) ) { | 
| 1188 | 0 |  |  |  |  | 0 | my @ha; | 
| 1189 | 0 | 0 |  |  |  | 0 | if ( defined( my $path = find_package_pod( $arg ) ) ) { | 
| 1190 | 0 |  |  |  |  | 0 | push @ha, '-input' => $path; | 
| 1191 |  |  |  |  |  |  | } | 
| 1192 | 0 |  |  |  |  | 0 | my $stdout = $self->{frame}[-1]{localout}; | 
| 1193 | 0 | 0 | 0 |  |  | 0 | if (openhandle $stdout && !-t $stdout) { | 
| 1194 | 0 |  |  |  |  | 0 | push @ha, -output => $stdout; | 
| 1195 |  |  |  |  |  |  | } | 
| 1196 |  |  |  |  |  |  | Pod::Usage::pod2usage ( | 
| 1197 | 0 |  |  |  |  | 0 | -verbose => 2, -exitval => 'NOEXIT', @ha); | 
| 1198 |  |  |  |  |  |  | } else { | 
| 1199 |  |  |  |  |  |  | # This should never happen, since Pod::Usage is core | 
| 1200 |  |  |  |  |  |  | # since 5.6. On the other hand we have not declared it | 
| 1201 |  |  |  |  |  |  | # as a dependency, and some downstream packagers seem to | 
| 1202 |  |  |  |  |  |  | # think they know more than the author what should be in | 
| 1203 |  |  |  |  |  |  | # a package. | 
| 1204 |  |  |  |  |  |  | return <<'EOD' | 
| 1205 |  |  |  |  |  |  | No help available; Pod::Usage can not be loaded. | 
| 1206 |  |  |  |  |  |  | EOD | 
| 1207 | 0 |  |  |  |  | 0 | } | 
| 1208 |  |  |  |  |  |  | } | 
| 1209 | 0 |  |  |  |  | 0 | return; | 
| 1210 | 20 |  |  | 20 |  | 14323 | } | 
|  | 20 |  |  |  |  | 58 |  | 
|  | 20 |  |  |  |  | 126 |  | 
| 1211 |  |  |  |  |  |  |  | 
| 1212 |  |  |  |  |  |  | # The call to this is generated dynamically above, and there is no way | 
| 1213 |  |  |  |  |  |  | # Perl::Critic can find it. | 
| 1214 |  |  |  |  |  |  | sub _help_MacOS {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1215 | 0 |  |  | 0 |  | 0 | return <<'EOD'; | 
| 1216 |  |  |  |  |  |  |  | 
| 1217 |  |  |  |  |  |  | Normally, we would display the documentation for the satpass2 | 
| 1218 |  |  |  |  |  |  | script here. But unfortunately this depends on the ability to | 
| 1219 |  |  |  |  |  |  | spawn the perldoc command, and we do not have this ability under | 
| 1220 |  |  |  |  |  |  | Mac OS 9 and earlier. You can find the same thing online at | 
| 1221 |  |  |  |  |  |  | https://metacpan.org/release/Astro-App-Satpass2 | 
| 1222 |  |  |  |  |  |  |  | 
| 1223 |  |  |  |  |  |  | EOD | 
| 1224 |  |  |  |  |  |  | } | 
| 1225 |  |  |  |  |  |  |  | 
| 1226 |  |  |  |  |  |  | { | 
| 1227 |  |  |  |  |  |  | # This hash specifies the specific grammar passed to | 
| 1228 |  |  |  |  |  |  | # __infix_engine(). The keys are: | 
| 1229 |  |  |  |  |  |  | # {done} optional; called when parse is complete. | 
| 1230 |  |  |  |  |  |  | # {oper} defines operators. Values are hash refs with: | 
| 1231 |  |  |  |  |  |  | #	{handler} code that handles operator; | 
| 1232 |  |  |  |  |  |  | #	{validation} name of validation style (see {vld} below). | 
| 1233 |  |  |  |  |  |  | # {vld} defines operator validation. There must be a key for each | 
| 1234 |  |  |  |  |  |  | #	distinct value of {oper}{$name}{validation}. | 
| 1235 |  |  |  |  |  |  | # NOTE WELL | 
| 1236 |  |  |  |  |  |  | # Because if() has the Tweak( -unsatisfied ) attribute, any | 
| 1237 |  |  |  |  |  |  | # operators that have side effects will need to be aware of whether | 
| 1238 |  |  |  |  |  |  | # they are running inside an unsatisfied if(). | 
| 1239 |  |  |  |  |  |  | my %define = ( | 
| 1240 |  |  |  |  |  |  | done	=> sub { | 
| 1241 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1242 |  |  |  |  |  |  | my ( $self, undef, $ctx ) = @_; | 
| 1243 |  |  |  |  |  |  | @{ $ctx } | 
| 1244 |  |  |  |  |  |  | and $self->wail( q );; | 
| 1245 |  |  |  |  |  |  | return; | 
| 1246 |  |  |  |  |  |  | }, | 
| 1247 |  |  |  |  |  |  | oper	=> { | 
| 1248 |  |  |  |  |  |  | '('	=> { | 
| 1249 |  |  |  |  |  |  | handler	=> sub { | 
| 1250 |  |  |  |  |  |  | my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1251 |  |  |  |  |  |  | my $want = delete $ctx->[-1]{want}; | 
| 1252 |  |  |  |  |  |  | defined $want | 
| 1253 |  |  |  |  |  |  | or $want = 1; | 
| 1254 |  |  |  |  |  |  | push @{ $ctx }, { | 
| 1255 |  |  |  |  |  |  | want	=> $want, | 
| 1256 |  |  |  |  |  |  | value	=> [], | 
| 1257 |  |  |  |  |  |  | }; | 
| 1258 |  |  |  |  |  |  | $ctx->[-2]{shortcut} | 
| 1259 |  |  |  |  |  |  | and $ctx->[1]{shortcut} = $ctx->[-2]{shortcut}; | 
| 1260 |  |  |  |  |  |  | my $depth = @{ $ctx }; | 
| 1261 |  |  |  |  |  |  | while ( $depth <= @{ $ctx } ) { | 
| 1262 |  |  |  |  |  |  | $self->_infix_engine_dispatch( $def, $ctx, $tokens ); | 
| 1263 |  |  |  |  |  |  | } | 
| 1264 |  |  |  |  |  |  | return; | 
| 1265 |  |  |  |  |  |  | }, | 
| 1266 |  |  |  |  |  |  | }, | 
| 1267 |  |  |  |  |  |  | ')'	=> { | 
| 1268 |  |  |  |  |  |  | handler	=> sub { | 
| 1269 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1270 |  |  |  |  |  |  | my ( $self, undef, $ctx ) = @_; | 
| 1271 |  |  |  |  |  |  | @{ $ctx } | 
| 1272 |  |  |  |  |  |  | or $self->wail( 'Unpaired right parentheses' ); | 
| 1273 |  |  |  |  |  |  | $ctx->[-1]{want} == @{ $ctx->[-1]{value} } | 
| 1274 |  |  |  |  |  |  | or $self->wail( | 
| 1275 |  |  |  |  |  |  | "Expected $ctx->[-1]{want} value(s), got " . | 
| 1276 |  |  |  |  |  |  | scalar @{ $ctx->[-1]{value} } ); | 
| 1277 |  |  |  |  |  |  | push @{ $ctx->[-2]{value} }, @{ $ctx->[-1]{value} }; | 
| 1278 |  |  |  |  |  |  | pop @{ $ctx }; | 
| 1279 |  |  |  |  |  |  | return; | 
| 1280 |  |  |  |  |  |  | }, | 
| 1281 |  |  |  |  |  |  | }, | 
| 1282 |  |  |  |  |  |  | '-n' => { | 
| 1283 |  |  |  |  |  |  | handler => sub { | 
| 1284 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1285 |  |  |  |  |  |  | my ( undef, undef, $ctx, $tokens ) = @_; | 
| 1286 |  |  |  |  |  |  | my $v = shift @{ $tokens }; | 
| 1287 |  |  |  |  |  |  | defined $v | 
| 1288 |  |  |  |  |  |  | or $v = ''; | 
| 1289 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, '' ne $v; | 
| 1290 |  |  |  |  |  |  | }, | 
| 1291 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1292 |  |  |  |  |  |  | }, | 
| 1293 |  |  |  |  |  |  | '-z' => { | 
| 1294 |  |  |  |  |  |  | handler => sub { | 
| 1295 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1296 |  |  |  |  |  |  | my ( undef, undef, $ctx, $tokens ) = @_; | 
| 1297 |  |  |  |  |  |  | my $v = shift @{ $tokens }; | 
| 1298 |  |  |  |  |  |  | defined $v | 
| 1299 |  |  |  |  |  |  | or $v = ''; | 
| 1300 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, '' eq $v; | 
| 1301 |  |  |  |  |  |  | }, | 
| 1302 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1303 |  |  |  |  |  |  | }, | 
| 1304 |  |  |  |  |  |  | and	=> { | 
| 1305 |  |  |  |  |  |  | handler	=> sub { | 
| 1306 |  |  |  |  |  |  | my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1307 |  |  |  |  |  |  | $ctx->[-1]{value}[-1] | 
| 1308 |  |  |  |  |  |  | or $ctx->[-1]{shortcut} = 1; | 
| 1309 |  |  |  |  |  |  | $self->_infix_engine_dispatch( $def, $ctx, $tokens ); | 
| 1310 |  |  |  |  |  |  | # For some reason the following has to be done in | 
| 1311 |  |  |  |  |  |  | # two statements, or both operands remain on the | 
| 1312 |  |  |  |  |  |  | # stack. | 
| 1313 |  |  |  |  |  |  | my $ro = pop @{ $ctx->[-1]{value} }; | 
| 1314 |  |  |  |  |  |  | $ctx->[-1]{value}[-1] &&= $ro | 
| 1315 |  |  |  |  |  |  | unless delete $ctx->[-1]{shortcut}; | 
| 1316 |  |  |  |  |  |  | return; | 
| 1317 |  |  |  |  |  |  | }, | 
| 1318 |  |  |  |  |  |  | validation	=> 'infix', | 
| 1319 |  |  |  |  |  |  | }, | 
| 1320 |  |  |  |  |  |  | attr	=> { | 
| 1321 |  |  |  |  |  |  | handler	=> sub { | 
| 1322 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1323 |  |  |  |  |  |  | my ( $self, undef, $ctx, $tokens ) = @_; | 
| 1324 |  |  |  |  |  |  | my $attr = shift @{ $tokens }; | 
| 1325 |  |  |  |  |  |  | my $val; | 
| 1326 |  |  |  |  |  |  | $ctx->[-1]{shortcut} | 
| 1327 |  |  |  |  |  |  | or $val = $self->_attribute_value( $attr ); | 
| 1328 |  |  |  |  |  |  | NULL_REF eq ref $val | 
| 1329 |  |  |  |  |  |  | and $self->wail( "No such attribute as '$attr'" ); | 
| 1330 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, $val; | 
| 1331 |  |  |  |  |  |  | return; | 
| 1332 |  |  |  |  |  |  | }, | 
| 1333 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1334 |  |  |  |  |  |  | }, | 
| 1335 |  |  |  |  |  |  | env	=> { | 
| 1336 |  |  |  |  |  |  | handler	=> sub { | 
| 1337 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1338 |  |  |  |  |  |  | my ( undef, undef, $ctx, $tokens ) = @_; | 
| 1339 |  |  |  |  |  |  | my $name = shift @{ $tokens }; | 
| 1340 |  |  |  |  |  |  | my $val; | 
| 1341 |  |  |  |  |  |  | $ctx->[-1]{shortcut} | 
| 1342 |  |  |  |  |  |  | or $val = $ENV{$name}; | 
| 1343 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, $val; | 
| 1344 |  |  |  |  |  |  | return; | 
| 1345 |  |  |  |  |  |  | }, | 
| 1346 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1347 |  |  |  |  |  |  | }, | 
| 1348 |  |  |  |  |  |  | loaded	=> { | 
| 1349 |  |  |  |  |  |  | handler	=> sub { | 
| 1350 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1351 |  |  |  |  |  |  | my ( $self, undef, $ctx, $tokens ) = @_; | 
| 1352 |  |  |  |  |  |  | my $name = shift @{ $tokens }; | 
| 1353 |  |  |  |  |  |  | my @loaded; | 
| 1354 |  |  |  |  |  |  | $ctx->[-1]{shortcut} | 
| 1355 |  |  |  |  |  |  | or @loaded = $self->__choose( | 
| 1356 |  |  |  |  |  |  | { bodies	=> 1 }, | 
| 1357 |  |  |  |  |  |  | [ $name ], | 
| 1358 |  |  |  |  |  |  | ); | 
| 1359 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, scalar @loaded; | 
| 1360 |  |  |  |  |  |  | return; | 
| 1361 |  |  |  |  |  |  | }, | 
| 1362 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1363 |  |  |  |  |  |  | }, | 
| 1364 |  |  |  |  |  |  | not	=> { | 
| 1365 |  |  |  |  |  |  | handler	=> sub { | 
| 1366 |  |  |  |  |  |  | my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1367 |  |  |  |  |  |  | $self->_infix_engine_dispatch( $def, $ctx, $tokens ); | 
| 1368 |  |  |  |  |  |  | $ctx->[-1]{value}[-1] = ! $ctx->[-1]{value}[-1]; | 
| 1369 |  |  |  |  |  |  | return; | 
| 1370 |  |  |  |  |  |  | }, | 
| 1371 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1372 |  |  |  |  |  |  | }, | 
| 1373 |  |  |  |  |  |  | or	=> { | 
| 1374 |  |  |  |  |  |  | handler	=> sub { | 
| 1375 |  |  |  |  |  |  | my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1376 |  |  |  |  |  |  | $ctx->[-1]{value}[-1] | 
| 1377 |  |  |  |  |  |  | and $ctx->[-1]{shortcut} = 1; | 
| 1378 |  |  |  |  |  |  | $self->_infix_engine_dispatch( $def, $ctx, $tokens ); | 
| 1379 |  |  |  |  |  |  | # For some reason the following has to be done in | 
| 1380 |  |  |  |  |  |  | # two statements, or both operands remain on the | 
| 1381 |  |  |  |  |  |  | # stack. | 
| 1382 |  |  |  |  |  |  | my $ro = pop @{ $ctx->[-1]{value} }; | 
| 1383 |  |  |  |  |  |  | $ctx->[-1]{value}[-1] ||= $ro | 
| 1384 |  |  |  |  |  |  | unless delete $ctx->[-1]{shortcut}; | 
| 1385 |  |  |  |  |  |  | return; | 
| 1386 |  |  |  |  |  |  | }, | 
| 1387 |  |  |  |  |  |  | validation	=> 'infix', | 
| 1388 |  |  |  |  |  |  | }, | 
| 1389 |  |  |  |  |  |  | os	=> { | 
| 1390 |  |  |  |  |  |  | handler	=> sub { | 
| 1391 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1392 |  |  |  |  |  |  | my ( undef, undef, $ctx, $tokens ) = @_; | 
| 1393 |  |  |  |  |  |  | my $re = qr< \A \Q$^O\E \z >smxi; | 
| 1394 |  |  |  |  |  |  | my $rslt = 0; | 
| 1395 |  |  |  |  |  |  | my $name = shift @{ $tokens }; | 
| 1396 |  |  |  |  |  |  | unless ( $ctx->[-1]{shortcut} ) { | 
| 1397 |  |  |  |  |  |  | foreach my $os ( split qr< [|] >smx, $name ) { | 
| 1398 |  |  |  |  |  |  | $os =~ $re | 
| 1399 |  |  |  |  |  |  | or next; | 
| 1400 |  |  |  |  |  |  | $rslt = 1; | 
| 1401 |  |  |  |  |  |  | last; | 
| 1402 |  |  |  |  |  |  | } | 
| 1403 |  |  |  |  |  |  | } | 
| 1404 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, $rslt; | 
| 1405 |  |  |  |  |  |  | return; | 
| 1406 |  |  |  |  |  |  | }, | 
| 1407 |  |  |  |  |  |  | validation	=> 'prefix', | 
| 1408 |  |  |  |  |  |  | }, | 
| 1409 |  |  |  |  |  |  | then	=> { | 
| 1410 |  |  |  |  |  |  | handler	=> sub { | 
| 1411 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1412 |  |  |  |  |  |  | my ( $self, undef, $ctx, $tokens ) = @_; | 
| 1413 |  |  |  |  |  |  | 1 == @{ $ctx } | 
| 1414 |  |  |  |  |  |  | or $self->wail( 'Unclosed left parentheses' ); | 
| 1415 |  |  |  |  |  |  | my $last = pop @{ $ctx }; | 
| 1416 |  |  |  |  |  |  | my @arg = splice @{ $tokens }; | 
| 1417 |  |  |  |  |  |  | if ( $last->{dispatch} ) { | 
| 1418 |  |  |  |  |  |  | $self->_dispatch_check( if => $arg[0] ); | 
| 1419 |  |  |  |  |  |  | $self->_frame_push( if => [], { | 
| 1420 |  |  |  |  |  |  | condition	=> $last->{value}[-1], | 
| 1421 |  |  |  |  |  |  | }, | 
| 1422 |  |  |  |  |  |  | ); | 
| 1423 |  |  |  |  |  |  | $self->_add_post_dispatch( sub { | 
| 1424 |  |  |  |  |  |  | $self->_frame_pop( if => undef ); | 
| 1425 |  |  |  |  |  |  | }, | 
| 1426 |  |  |  |  |  |  | ); | 
| 1427 |  |  |  |  |  |  | return $self->dispatch( @arg ); | 
| 1428 |  |  |  |  |  |  | } else { | 
| 1429 |  |  |  |  |  |  | $self->_twiddle_condition( $last->{value}[-1] ); | 
| 1430 |  |  |  |  |  |  | } | 
| 1431 |  |  |  |  |  |  | }, | 
| 1432 |  |  |  |  |  |  | validation	=> 'terminal', | 
| 1433 |  |  |  |  |  |  | }, | 
| 1434 |  |  |  |  |  |  | }, | 
| 1435 |  |  |  |  |  |  | val	=> sub { | 
| 1436 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tkn, $tokens ) = @_; | 
| 1437 |  |  |  |  |  |  | my ( undef, undef, $ctx, $tkn ) = @_; | 
| 1438 |  |  |  |  |  |  | push @{ $ctx->[-1]{value} }, $tkn; | 
| 1439 |  |  |  |  |  |  | return; | 
| 1440 |  |  |  |  |  |  | }, | 
| 1441 |  |  |  |  |  |  | vld	=> { | 
| 1442 |  |  |  |  |  |  | infix	=> sub { | 
| 1443 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tkn, $tokens ) = @_; | 
| 1444 |  |  |  |  |  |  | my ( $self, undef, $ctx, $tkn, $tokens ) = @_; | 
| 1445 |  |  |  |  |  |  | @{ $ctx->[-1]{value} } | 
| 1446 |  |  |  |  |  |  | or $self->wail( "'$tkn' requires a left argument" ); | 
| 1447 |  |  |  |  |  |  | @{ $tokens } | 
| 1448 |  |  |  |  |  |  | or $self->wail( "'$tkn' requires a right argument" ); | 
| 1449 |  |  |  |  |  |  | return; | 
| 1450 |  |  |  |  |  |  | }, | 
| 1451 |  |  |  |  |  |  | prefix	=> sub { | 
| 1452 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tkn, $tokens ) = @_; | 
| 1453 |  |  |  |  |  |  | my ( $self, undef, undef, $tkn, $tokens ) = @_; | 
| 1454 |  |  |  |  |  |  | @{ $tokens } | 
| 1455 |  |  |  |  |  |  | or $self->wail( "'$tkn' requires an argument" ); | 
| 1456 |  |  |  |  |  |  | return; | 
| 1457 |  |  |  |  |  |  | }, | 
| 1458 |  |  |  |  |  |  | terminal	=> sub { | 
| 1459 |  |  |  |  |  |  | # my ( $self, $def, $ctx, $tkn, $tokens ) = @_; | 
| 1460 |  |  |  |  |  |  | my ( $self, undef, $ctx, $tkn, $tokens ) = @_; | 
| 1461 |  |  |  |  |  |  | @{ $ctx->[-1]{value} } | 
| 1462 |  |  |  |  |  |  | or $self->wail( "'$tkn' requires a left argument" ); | 
| 1463 |  |  |  |  |  |  | if ( $ctx->[-1]{dispatch} ) { | 
| 1464 |  |  |  |  |  |  | @{ $tokens } | 
| 1465 |  |  |  |  |  |  | or $self->wail( "Command required after '$tkn'" ); | 
| 1466 |  |  |  |  |  |  | } else { | 
| 1467 |  |  |  |  |  |  | @{ $tokens } | 
| 1468 |  |  |  |  |  |  | and $self->wail( "Command not allowed after '$tkn'" ); | 
| 1469 |  |  |  |  |  |  | } | 
| 1470 |  |  |  |  |  |  | return; | 
| 1471 |  |  |  |  |  |  | } | 
| 1472 |  |  |  |  |  |  | }, | 
| 1473 |  |  |  |  |  |  | ); | 
| 1474 |  |  |  |  |  |  |  | 
| 1475 |  |  |  |  |  |  | sub elsif : method Verb() Tweak( -unsatisfied ) {	## no critic (ProhibitBuiltInHomonyms) | 
| 1476 | 2 |  |  | 2 | 1 | 8 | my ( $self, @args ) = @_; | 
| 1477 |  |  |  |  |  |  | @args | 
| 1478 | 2 | 50 |  |  |  | 7 | or $self->wail( 'Arguments required' ); | 
| 1479 |  |  |  |  |  |  |  | 
| 1480 | 2 |  |  |  |  | 33 | @{ $self->{frame} } > 1 | 
| 1481 |  |  |  |  |  |  | and 'begin' eq $self->{frame}[-1]{type} | 
| 1482 |  |  |  |  |  |  | and 'if' eq $self->{frame}[-2]{type} | 
| 1483 | 2 | 50 | 33 |  |  | 6 | or $self->wail( 'Elsif without if ... then begin' ); | 
|  |  |  | 33 |  |  |  |  | 
| 1484 |  |  |  |  |  |  |  | 
| 1485 | 2 |  |  |  |  | 11 | my @ctx = ( { | 
| 1486 |  |  |  |  |  |  | dispatch	=> 0, | 
| 1487 |  |  |  |  |  |  | value	=> [], | 
| 1488 |  |  |  |  |  |  | } ); | 
| 1489 |  |  |  |  |  |  |  | 
| 1490 |  |  |  |  |  |  | # If any previous if() or elsif() evaluates true, we do not | 
| 1491 |  |  |  |  |  |  | # evaluate subsequent elsif() calls. | 
| 1492 |  |  |  |  |  |  | $self->{frame}[-2]{condition} | 
| 1493 | 2 | 50 |  |  |  | 8 | and return; | 
| 1494 | 2 |  |  |  |  | 9 | return $self->__infix_engine( \%define, \@ctx, @args ); | 
| 1495 | 20 |  |  | 20 |  | 46545 | } | 
|  | 20 |  |  |  |  | 86 |  | 
|  | 20 |  |  |  |  | 559 |  | 
| 1496 |  |  |  |  |  |  |  | 
| 1497 |  |  |  |  |  |  | sub if : method Verb() Tweak( -unsatisfied ) {	## no critic (ProhibitBuiltInHomonyms) | 
| 1498 | 22 |  |  | 22 | 1 | 74 | my ( $self, @args ) = @_; | 
| 1499 |  |  |  |  |  |  | @args | 
| 1500 | 22 | 50 |  |  |  | 49 | or $self->wail( 'Arguments required' ); | 
| 1501 | 22 |  |  |  |  | 89 | my @ctx = ( { | 
| 1502 |  |  |  |  |  |  | dispatch	=> 1, | 
| 1503 |  |  |  |  |  |  | value	=> [], | 
| 1504 |  |  |  |  |  |  | } ); | 
| 1505 | 22 |  |  |  |  | 71 | return $self->__infix_engine( \%define, \@ctx, @args ); | 
| 1506 | 20 |  |  | 20 |  | 6560 | } | 
|  | 20 |  |  |  |  | 72 |  | 
|  | 20 |  |  |  |  | 121 |  | 
| 1507 |  |  |  |  |  |  | } | 
| 1508 |  |  |  |  |  |  |  | 
| 1509 |  |  |  |  |  |  | sub init { | 
| 1510 | 0 |  |  | 0 | 1 | 0 | my ( $self, @args ) = @_; | 
| 1511 |  |  |  |  |  |  |  | 
| 1512 | 0 | 0 |  |  |  | 0 | my $opt = HASH_REF eq ref $args[0] ? shift @args : {}; | 
| 1513 | 0 |  |  |  |  | 0 | my $init_file = shift @args; | 
| 1514 |  |  |  |  |  |  |  | 
| 1515 | 0 |  |  |  |  | 0 | $self->{initfile} = undef; | 
| 1516 |  |  |  |  |  |  |  | 
| 1517 | 0 | 0 |  |  |  | 0 | foreach ( | 
| 1518 |  |  |  |  |  |  | defined $init_file ? ( | 
| 1519 |  |  |  |  |  |  | sub { | 
| 1520 |  |  |  |  |  |  | # A missing init file is only an error if it was | 
| 1521 |  |  |  |  |  |  | # specified explicitly. | 
| 1522 | 0 | 0 | 0 | 0 |  | 0 | -e $init_file | 
| 1523 |  |  |  |  |  |  | and not -d _ | 
| 1524 |  |  |  |  |  |  | or $self->wail( | 
| 1525 |  |  |  |  |  |  | "Initialization file $init_file not found, or is a directory" | 
| 1526 |  |  |  |  |  |  | ); | 
| 1527 |  |  |  |  |  |  | return ( $init_file, $opt->{level1} ) | 
| 1528 | 0 |  |  |  |  | 0 | }, | 
| 1529 |  |  |  |  |  |  | ) : ( | 
| 1530 | 0 |  |  | 0 |  | 0 | sub { return $ENV{SATPASS2INI} }, | 
| 1531 | 0 |  |  | 0 |  | 0 | sub { $self->initfile( { quiet => 1 } ) }, | 
| 1532 | 0 |  |  | 0 |  | 0 | sub { return ( $ENV{SATPASSINI}, 1 ) }, | 
| 1533 |  |  |  |  |  |  | \&_init_file_01, | 
| 1534 |  |  |  |  |  |  | ) | 
| 1535 |  |  |  |  |  |  | ) { | 
| 1536 |  |  |  |  |  |  |  | 
| 1537 | 0 |  |  |  |  | 0 | my ( $fn, $level1 ) = $_->($self); | 
| 1538 | 0 | 0 |  |  |  | 0 | my $reader = $self->_file_reader( $fn, { optional => 1 } ) | 
| 1539 |  |  |  |  |  |  | or next; | 
| 1540 | 0 |  |  |  |  | 0 | $self->{initfile} = $fn; | 
| 1541 | 0 |  |  |  |  | 0 | return $self->source( { level1 => $level1 }, $reader ); | 
| 1542 |  |  |  |  |  |  |  | 
| 1543 |  |  |  |  |  |  | } | 
| 1544 |  |  |  |  |  |  |  | 
| 1545 | 0 |  |  |  |  | 0 | return; | 
| 1546 |  |  |  |  |  |  | } | 
| 1547 |  |  |  |  |  |  |  | 
| 1548 |  |  |  |  |  |  | sub initfile : Verb( create-directory! quiet! ) { | 
| 1549 | 0 |  |  | 0 | 1 | 0 | my ( $self, $opt ) = __arguments( @_ );	# @args unused | 
| 1550 |  |  |  |  |  |  |  | 
| 1551 |  |  |  |  |  |  | my $init_dir = my_dist_config( | 
| 1552 | 0 |  |  |  |  | 0 | { create => $opt->{'create-directory'} } ); | 
| 1553 |  |  |  |  |  |  |  | 
| 1554 |  |  |  |  |  |  | defined $init_dir | 
| 1555 | 0 | 0 |  |  |  | 0 | or do { | 
| 1556 | 0 | 0 |  |  |  | 0 | $opt->{quiet} and return; | 
| 1557 | 0 |  |  |  |  | 0 | $self->wail( | 
| 1558 |  |  |  |  |  |  | 'Init file directory not found' ); | 
| 1559 |  |  |  |  |  |  | }; | 
| 1560 |  |  |  |  |  |  |  | 
| 1561 | 0 |  |  |  |  | 0 | return File::Spec->catfile( $init_dir, 'satpass2rc' ); | 
| 1562 | 20 |  |  | 20 |  | 15979 | } | 
|  | 20 |  |  |  |  | 66 |  | 
|  | 20 |  |  |  |  | 1437 |  | 
| 1563 |  |  |  |  |  |  |  | 
| 1564 |  |  |  |  |  |  | sub _in_unsatisfied_if { | 
| 1565 | 290 |  |  | 290 |  | 573 | my ( $self ) = @_; | 
| 1566 | 290 | 50 |  |  |  | 430 | return @{ $self->{frame} } ? $self->{frame}[-1]{unsatisfied_if} : 0; | 
|  | 290 |  |  |  |  | 1190 |  | 
| 1567 |  |  |  |  |  |  | } | 
| 1568 |  |  |  |  |  |  |  | 
| 1569 |  |  |  |  |  |  | # This is a generalized infix expression engine. It does not implement | 
| 1570 |  |  |  |  |  |  | # operator precedence and is therefore very small. The arguments are: | 
| 1571 |  |  |  |  |  |  | #   - $self is the invocant, which must be an | 
| 1572 |  |  |  |  |  |  | #     Astro::App::Satpass2::Copier. | 
| 1573 |  |  |  |  |  |  | #   - $def is the hash that defines the grammar. This provides the | 
| 1574 |  |  |  |  |  |  | #     following keys: | 
| 1575 |  |  |  |  |  |  | #     {done} is an optional code reference. If present, the code | 
| 1576 |  |  |  |  |  |  | #	 reference is called once the parse is complete, and passed | 
| 1577 |  |  |  |  |  |  | #	 ( $self, $def, $ctx, \@tokens ). It returns nothing. The intent | 
| 1578 |  |  |  |  |  |  | #	 is to throw an exception if the parse is incomplete. | 
| 1579 |  |  |  |  |  |  | #     {oper} defines the operators. This is a hash keyed by the literal | 
| 1580 |  |  |  |  |  |  | #        operator (i.e. '+' to implement a '+' operator), and having the | 
| 1581 |  |  |  |  |  |  | #        following values: | 
| 1582 |  |  |  |  |  |  | #        {handler} is a required code reference, which implements the | 
| 1583 |  |  |  |  |  |  | #           operator. It is passed ( $self, $def, $ctx, \@tokens ). The | 
| 1584 |  |  |  |  |  |  | #           @tokens do not include the operator itself. | 
| 1585 |  |  |  |  |  |  | #        {validation} is an optional validation specification. If | 
| 1586 |  |  |  |  |  |  | #           present it is a key in the {vld} (see below). | 
| 1587 |  |  |  |  |  |  | #     {val} is an optional code reference. If present, it is called if a | 
| 1588 |  |  |  |  |  |  | #        token is not recognized as an operator, and passed ( $self, | 
| 1589 |  |  |  |  |  |  | #        $def, $ctx, \@tokens ). The @tokens include the unrecognized | 
| 1590 |  |  |  |  |  |  | #        token, which is presumed to be a value, and must be removed | 
| 1591 |  |  |  |  |  |  | #        from @tokens. | 
| 1592 |  |  |  |  |  |  | #     {vld} is a hash of validators. The keys are values in the | 
| 1593 |  |  |  |  |  |  | #        {validation} key documented under {oper} (above), and the | 
| 1594 |  |  |  |  |  |  | #        values are code references which are called with ( $self, $ctx, | 
| 1595 |  |  |  |  |  |  | #        $tkn, \@tokens ) where $tkn is the token being validated, and | 
| 1596 |  |  |  |  |  |  | #        @tokens is the rest of the tokens. This hash must exist if the | 
| 1597 |  |  |  |  |  |  | #        {validation} key is used in {oper}; otherwise it is optional. | 
| 1598 |  |  |  |  |  |  | #   - $ctx is context for the operations. It is not used by the engine | 
| 1599 |  |  |  |  |  |  | #     itself, but the individual operator code will need to use it as | 
| 1600 |  |  |  |  |  |  | #     context for the parse.  See if() for an example. | 
| 1601 |  |  |  |  |  |  | #   - @tokens are the tokens to be evaluated by the engine. | 
| 1602 |  |  |  |  |  |  | sub __infix_engine { | 
| 1603 | 24 |  |  | 24 |  | 74 | my ( $self, $def, $ctx, @tokens ) = @_; | 
| 1604 |  |  |  |  |  |  | @tokens | 
| 1605 | 24 | 50 |  |  |  | 51 | or $self->wail( 'Nothing to compute' ); | 
| 1606 | 24 |  |  |  |  | 35 | my $rslt; | 
| 1607 | 24 |  |  |  |  | 54 | while ( @tokens ) { | 
| 1608 | 50 |  |  |  |  | 123 | $rslt = $self->_infix_engine_dispatch( $def, $ctx, \@tokens ); | 
| 1609 |  |  |  |  |  |  | } | 
| 1610 |  |  |  |  |  |  | $def->{done} | 
| 1611 | 24 | 50 |  |  |  | 106 | and $def->{done}->( $self, $def, $ctx, \@tokens ); | 
| 1612 | 24 |  |  |  |  | 81 | return $rslt; | 
| 1613 |  |  |  |  |  |  | } | 
| 1614 |  |  |  |  |  |  |  | 
| 1615 |  |  |  |  |  |  | sub _infix_engine_dispatch { | 
| 1616 | 61 |  |  | 61 |  | 116 | my ( $self, $def, $ctx, $tokens ) = @_; | 
| 1617 | 61 | 50 |  |  |  | 82 | @{ $tokens } | 
|  | 61 |  |  |  |  | 125 |  | 
| 1618 |  |  |  |  |  |  | or return; | 
| 1619 | 61 |  |  |  |  | 94 | my $tkn = shift @{ $tokens }; | 
|  | 61 |  |  |  |  | 109 |  | 
| 1620 | 61 | 100 |  |  |  | 185 | if ( my $info = $def->{oper}{$tkn} ) { | 
|  |  | 50 |  |  |  |  |  | 
| 1621 |  |  |  |  |  |  | $info->{validation} | 
| 1622 | 57 | 100 |  |  |  | 262 | and $def->{vld}{ $info->{validation} }->( | 
| 1623 |  |  |  |  |  |  | $self, $def, $ctx, $tkn, $tokens ); | 
| 1624 | 57 |  |  |  |  | 152 | return $info->{handler}->( $self, $def, $ctx, $tokens ); | 
| 1625 |  |  |  |  |  |  | } elsif ( $def->{val} ) { | 
| 1626 | 4 |  |  |  |  | 13 | return $def->{val}->( $self, $def, $ctx, $tkn, $tokens ); | 
| 1627 |  |  |  |  |  |  | } else { | 
| 1628 | 0 |  |  |  |  | 0 | $self->wail( "Unrecognized token '$tkn'" ); | 
| 1629 |  |  |  |  |  |  | } | 
| 1630 | 0 |  |  |  |  | 0 | return;	# We can't get here, but Perl::Critic does not know this. | 
| 1631 |  |  |  |  |  |  | } | 
| 1632 |  |  |  |  |  |  |  | 
| 1633 |  |  |  |  |  |  | #	$file_name = _init_file_01() | 
| 1634 |  |  |  |  |  |  | # | 
| 1635 |  |  |  |  |  |  | #	This subroutine returns the first alternate init file name, | 
| 1636 |  |  |  |  |  |  | #	which is the standard name for the Astro-satpass 'satpass' | 
| 1637 |  |  |  |  |  |  | #	script. If called in list context it returns not only the name, | 
| 1638 |  |  |  |  |  |  | #	but a 1 to tell the caller this is a 'level1' file. | 
| 1639 |  |  |  |  |  |  |  | 
| 1640 |  |  |  |  |  |  | sub _init_file_01 { | 
| 1641 | 0 | 0 | 0 | 0 |  | 0 | my $inifn = $^O eq 'MSWin32' || $^O eq 'VMS' || $^O eq 'MacOS' ? | 
| 1642 |  |  |  |  |  |  | 'satpass.ini' : '.satpass'; | 
| 1643 |  |  |  |  |  |  | my $inifile = $^O eq 'VMS' ? "SYS\$LOGIN:$inifn" : | 
| 1644 |  |  |  |  |  |  | $^O eq 'MacOS' ? $inifn : | 
| 1645 |  |  |  |  |  |  | $ENV{HOME} ? "$ENV{HOME}/$inifn" : | 
| 1646 |  |  |  |  |  |  | $ENV{LOGDIR} ? "$ENV{LOGDIR}/$inifn" : | 
| 1647 | 0 | 0 |  |  |  | 0 | $ENV{USERPROFILE} ? "$ENV{USERPROFILE}" : undef; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1648 | 0 | 0 |  |  |  | 0 | return wantarray ? ( $inifile, 1 ) : $inifile; | 
| 1649 |  |  |  |  |  |  | } | 
| 1650 |  |  |  |  |  |  |  | 
| 1651 |  |  |  |  |  |  | sub list : Verb( choose=s@ ) { | 
| 1652 | 7 |  |  | 7 | 1 | 46 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 1653 |  |  |  |  |  |  |  | 
| 1654 |  |  |  |  |  |  | @args | 
| 1655 |  |  |  |  |  |  | and not $opt->{choose} | 
| 1656 | 7 | 50 | 33 |  |  | 60 | and $opt->{choose} = \@args; | 
| 1657 | 7 |  |  |  |  | 54 | my @bodies = $self->__choose( $opt->{choose}, $self->{bodies} ); | 
| 1658 |  |  |  |  |  |  |  | 
| 1659 |  |  |  |  |  |  | @bodies | 
| 1660 | 7 | 100 |  |  |  | 90 | and return $self->__format_data( | 
| 1661 |  |  |  |  |  |  | list => \@bodies, $opt ); | 
| 1662 |  |  |  |  |  |  |  | 
| 1663 |  |  |  |  |  |  | $self->{warn_on_empty} | 
| 1664 | 2 | 50 |  |  |  | 29 | and $self->whinge( 'The observing list is empty' ); | 
| 1665 |  |  |  |  |  |  |  | 
| 1666 | 2 |  |  |  |  | 12 | return; | 
| 1667 | 20 |  |  | 20 |  | 20547 | } | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 2804 |  | 
| 1668 |  |  |  |  |  |  |  | 
| 1669 |  |  |  |  |  |  | sub load : Verb( verbose! ) { | 
| 1670 | 6 |  |  | 6 | 1 | 31 | my ( $self, $opt, @names ) = __arguments( @_ ); | 
| 1671 | 6 | 50 |  |  |  | 46 | @names or $self->wail( 'No file names specified' ); | 
| 1672 |  |  |  |  |  |  |  | 
| 1673 | 6 |  |  |  |  | 50 | my $attrs = { | 
| 1674 |  |  |  |  |  |  | illum	=> $self->get( 'illum' ), | 
| 1675 |  |  |  |  |  |  | model	=> $self->get( 'model' ), | 
| 1676 |  |  |  |  |  |  | sun	=> $self->_sky_object( 'sun' ), | 
| 1677 |  |  |  |  |  |  | }; | 
| 1678 |  |  |  |  |  |  |  | 
| 1679 | 6 |  |  |  |  | 701 | foreach my $fn ( @names ) { | 
| 1680 | 6 | 50 |  |  |  | 25 | $opt->{verbose} and warn "Loading $fn\n"; | 
| 1681 | 6 |  |  |  |  | 64 | my $data = $self->_file_reader( $fn, { glob => 1 } ); | 
| 1682 | 5 |  |  |  |  | 339 | $self->__add_to_observing_list( | 
| 1683 |  |  |  |  |  |  | Astro::Coord::ECI::TLE->parse( $attrs, $data ) ); | 
| 1684 |  |  |  |  |  |  | } | 
| 1685 | 5 |  |  |  |  | 30 | return; | 
| 1686 | 20 |  |  | 20 |  | 6841 | } | 
|  | 20 |  |  |  |  | 1316 |  | 
|  | 20 |  |  |  |  | 2655 |  | 
| 1687 |  |  |  |  |  |  |  | 
| 1688 |  |  |  |  |  |  | sub localize : Verb( all|except! ) { | 
| 1689 | 1 |  |  | 1 | 1 | 4 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 1690 |  |  |  |  |  |  |  | 
| 1691 | 1 |  |  |  |  | 10 | foreach my $name ( @args ) { | 
| 1692 | 2 |  |  |  |  | 6 | $self->_attribute_exists( $name ); | 
| 1693 |  |  |  |  |  |  | } | 
| 1694 |  |  |  |  |  |  |  | 
| 1695 | 1 | 50 |  |  |  | 6 | if ( $opt->{all} ) { | 
| 1696 | 0 |  |  |  |  | 0 | my %except = map { $_ => 1 } @args; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1697 | 0 |  |  |  |  | 0 | @args = grep { ! $except{$_} } sort keys %mutator; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1698 |  |  |  |  |  |  | } | 
| 1699 |  |  |  |  |  |  |  | 
| 1700 | 1 |  |  |  |  | 4 | foreach my $name ( @args ) { | 
| 1701 | 2 |  |  |  |  | 9 | $self->_localize( $name ); | 
| 1702 |  |  |  |  |  |  | } | 
| 1703 |  |  |  |  |  |  |  | 
| 1704 | 1 |  |  |  |  | 3 | return; | 
| 1705 | 20 |  |  | 20 |  | 11189 | } | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 108 |  | 
| 1706 |  |  |  |  |  |  |  | 
| 1707 |  |  |  |  |  |  | sub _localize { | 
| 1708 | 2 |  |  | 2 |  | 5 | my ( $self, $key ) = @_; | 
| 1709 |  |  |  |  |  |  |  | 
| 1710 |  |  |  |  |  |  | my $val = exists $self->{$key} ? | 
| 1711 | 2 | 50 |  |  |  | 9 | $self->{$key} : | 
| 1712 |  |  |  |  |  |  | $self->get( $key ); | 
| 1713 | 2 | 50 | 33 |  |  | 11 | my $clone = ( blessed( $val ) && $val->can( 'clone' ) ) ? | 
|  |  | 50 |  |  |  |  |  | 
| 1714 |  |  |  |  |  |  | $val->clone() : | 
| 1715 |  |  |  |  |  |  | ref $val ? Clone::clone( $val ) : $val; | 
| 1716 |  |  |  |  |  |  |  | 
| 1717 | 2 |  |  |  |  | 8 | $self->{frame}[-1]{local}{$key} = $val; | 
| 1718 | 2 | 50 |  |  |  | 7 | if ( exists $self->{$key} ) { | 
| 1719 | 2 |  |  |  |  | 5 | $self->{$key} = $clone; | 
| 1720 |  |  |  |  |  |  | } else { | 
| 1721 | 0 |  |  |  |  | 0 | $self->set( $key => $clone ); | 
| 1722 |  |  |  |  |  |  | } | 
| 1723 |  |  |  |  |  |  |  | 
| 1724 | 2 |  |  |  |  | 5 | return; | 
| 1725 |  |  |  |  |  |  | } | 
| 1726 |  |  |  |  |  |  |  | 
| 1727 |  |  |  |  |  |  | sub location : Verb( dump! ) { | 
| 1728 | 3 |  |  | 3 | 1 | 25 | my ( $self, $opt ) = __arguments( @_ ); | 
| 1729 | 3 |  |  |  |  | 38 | return $self->__format_data( | 
| 1730 |  |  |  |  |  |  | location => $self->station(), $opt ); | 
| 1731 | 20 |  |  | 20 |  | 10145 | } | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 1414 |  | 
| 1732 |  |  |  |  |  |  |  | 
| 1733 |  |  |  |  |  |  | { | 
| 1734 |  |  |  |  |  |  |  | 
| 1735 |  |  |  |  |  |  | # TODO the %mac_cmd hash is only needed for level1 compatibility. | 
| 1736 |  |  |  |  |  |  | # Once that goes away, it can too PROVIDED we also drop the | 
| 1737 |  |  |  |  |  |  | # subcommand defaulting functionality. | 
| 1738 |  |  |  |  |  |  | # Subcommand defaulting dropped 2021-09-20 unless explicitly level1, | 
| 1739 |  |  |  |  |  |  | # after I discovered that my init file defined an unwanted macro | 
| 1740 |  |  |  |  |  |  | # when I mistyped 'define' as 'defined'. | 
| 1741 |  |  |  |  |  |  | my %mac_cmd; | 
| 1742 |  |  |  |  |  |  | { | 
| 1743 |  |  |  |  |  |  | my $stb = __PACKAGE__ . '::'; | 
| 1744 |  |  |  |  |  |  | my @cmdnam; | 
| 1745 |  |  |  |  |  |  | { | 
| 1746 | 20 |  |  | 20 |  | 4257 | no strict qw{ refs }; | 
|  | 20 |  |  |  |  | 48 |  | 
|  | 20 |  |  |  |  | 6997 |  | 
| 1747 |  |  |  |  |  |  | foreach my $entry ( keys %{ $stb } ) { | 
| 1748 |  |  |  |  |  |  | $entry =~ m/ \A _macro_sub_ ( \w+ ) /smx | 
| 1749 |  |  |  |  |  |  | or next; | 
| 1750 |  |  |  |  |  |  | # Strictly speaking I should make sure the {CODE} slot | 
| 1751 |  |  |  |  |  |  | # is occupied here. | 
| 1752 |  |  |  |  |  |  | push @cmdnam, $1; | 
| 1753 |  |  |  |  |  |  | } | 
| 1754 |  |  |  |  |  |  | } | 
| 1755 |  |  |  |  |  |  | my %abbr = abbrev(@cmdnam); | 
| 1756 |  |  |  |  |  |  | foreach (keys %abbr) { | 
| 1757 |  |  |  |  |  |  | $mac_cmd{'-' . $_} = $abbr{$_}; | 
| 1758 |  |  |  |  |  |  | } | 
| 1759 |  |  |  |  |  |  | foreach (@cmdnam) { | 
| 1760 |  |  |  |  |  |  | $mac_cmd{$_} = $_; | 
| 1761 |  |  |  |  |  |  | } | 
| 1762 |  |  |  |  |  |  | } | 
| 1763 |  |  |  |  |  |  |  | 
| 1764 |  |  |  |  |  |  | # NOTE that we must not define command options here, but on the | 
| 1765 |  |  |  |  |  |  | # individual _macro_sub_* methods. Or at least we must not define | 
| 1766 |  |  |  |  |  |  | # any command options here that get passed to the _macro_sub_* | 
| 1767 |  |  |  |  |  |  | # methods. | 
| 1768 |  |  |  |  |  |  | sub macro : Verb() Tweak( -completion _readline_complete_subcommand ) { | 
| 1769 | 29 |  |  | 29 | 1 | 94 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 1770 | 29 |  |  |  |  | 64 | my $cmd; | 
| 1771 | 29 | 50 |  |  |  | 118 | if (!@args) { | 
|  |  | 100 |  |  |  |  |  | 
| 1772 | 0 |  |  |  |  | 0 | $cmd = 'brief'; | 
| 1773 |  |  |  |  |  |  | } elsif ( $self->{frame}[-1]{level1} ) { | 
| 1774 | 8 | 50 |  |  |  | 56 | if ($mac_cmd{$args[0]}) { | 
|  |  | 50 |  |  |  |  |  | 
| 1775 | 0 |  |  |  |  | 0 | $cmd = $mac_cmd{shift @args}; | 
| 1776 |  |  |  |  |  |  | } elsif (@args > 1) { | 
| 1777 | 8 |  |  |  |  | 18 | $cmd = 'define'; | 
| 1778 |  |  |  |  |  |  | } else { | 
| 1779 | 0 |  |  |  |  | 0 | $cmd = 'list'; | 
| 1780 |  |  |  |  |  |  | } | 
| 1781 |  |  |  |  |  |  | } else { | 
| 1782 | 21 | 50 |  |  |  | 77 | defined( $cmd = $mac_cmd{ $args[0] } ) | 
| 1783 |  |  |  |  |  |  | or $cmd = $args[0]; | 
| 1784 | 21 |  |  |  |  | 38 | shift @args; | 
| 1785 |  |  |  |  |  |  | } | 
| 1786 |  |  |  |  |  |  |  | 
| 1787 | 29 | 50 |  |  |  | 159 | my $code = $self->can( "_macro_sub_$cmd" ) | 
| 1788 |  |  |  |  |  |  | or $self->wail( "Subcommand '$cmd' unknown" ); | 
| 1789 | 29 |  |  |  |  | 116 | return $code->( $self, @args ); | 
| 1790 | 20 |  |  | 20 |  | 187 | } | 
|  | 20 |  |  |  |  | 41 |  | 
|  | 20 |  |  |  |  | 87 |  | 
| 1791 |  |  |  |  |  |  | } | 
| 1792 |  |  |  |  |  |  |  | 
| 1793 |  |  |  |  |  |  | # Calls to the following _macro_sub_... methods are generated dynamically | 
| 1794 |  |  |  |  |  |  | # above, so there is no way Perl::Critic can find them. | 
| 1795 |  |  |  |  |  |  | sub _macro_sub_brief : Verb() Tweak( -completion _macro_list_complete ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1796 | 3 |  |  | 3 |  | 24 | my ( $self, undef, @args ) = __arguments( @_ ); | 
| 1797 | 3 |  |  |  |  | 8 | my $output; | 
| 1798 | 3 | 50 |  |  |  | 9 | foreach my $name (sort @args ? @args : keys %{$self->{macro}}) { | 
|  | 3 |  |  |  |  | 15 |  | 
| 1799 | 1 | 50 |  |  |  | 8 | $self->{macro}{$name} and $output .= $name . "\n"; | 
| 1800 |  |  |  |  |  |  | } | 
| 1801 | 3 |  |  |  |  | 10 | return $output; | 
| 1802 | 20 |  |  | 20 |  | 7176 | } | 
|  | 20 |  |  |  |  | 61 |  | 
|  | 20 |  |  |  |  | 99 |  | 
| 1803 |  |  |  |  |  |  |  | 
| 1804 |  |  |  |  |  |  | sub _macro_sub_define : Verb( completion=s@ ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1805 | 16 |  |  | 16 |  | 49 | my ( $self, $opt, $name, @args ) = __arguments( @_ ); | 
| 1806 | 16 |  |  |  |  | 34 | my $output; | 
| 1807 | 16 | 50 |  |  |  | 41 | defined $name | 
| 1808 |  |  |  |  |  |  | or return $self->__wail( 'You must provide a name for the macro' ); | 
| 1809 |  |  |  |  |  |  | @args | 
| 1810 | 16 | 50 |  |  |  | 43 | or return $self->__wail( 'You must provide a definition for the macro' ); | 
| 1811 |  |  |  |  |  |  |  | 
| 1812 | 16 | 50 | 33 |  |  | 100 | $name !~ m/ \W /smx | 
| 1813 |  |  |  |  |  |  | and $name !~ m/ \A _ /smx | 
| 1814 |  |  |  |  |  |  | or return $self->__wail("Invalid macro name '$name'"); | 
| 1815 |  |  |  |  |  |  |  | 
| 1816 |  |  |  |  |  |  | # NOTE the value of {def} used to be unescaped, but I do not now | 
| 1817 |  |  |  |  |  |  | # know why, and the implementation of \U and friends is more natural | 
| 1818 |  |  |  |  |  |  | # with this stripped out. | 
| 1819 |  |  |  |  |  |  | $self->{macro}{$name} = | 
| 1820 |  |  |  |  |  |  | Astro::App::Satpass2::Macro::Command->new( | 
| 1821 |  |  |  |  |  |  | name	=> $name, | 
| 1822 |  |  |  |  |  |  | parent	=> $self, | 
| 1823 |  |  |  |  |  |  | completion	=> $opt->{completion}, | 
| 1824 |  |  |  |  |  |  | def		=> \@args, | 
| 1825 |  |  |  |  |  |  | generate	=> \&_macro_define_generator, | 
| 1826 |  |  |  |  |  |  | level1	=> $self->{frame}[-1]{level1}, | 
| 1827 |  |  |  |  |  |  | warner	=> $self->{_warner}, | 
| 1828 | 16 |  |  |  |  | 167 | ); | 
| 1829 | 16 |  |  |  |  | 73 | return $output; | 
| 1830 | 20 |  |  | 20 |  | 9048 | } | 
|  | 20 |  |  |  |  | 54 |  | 
|  | 20 |  |  |  |  | 101 |  | 
| 1831 |  |  |  |  |  |  |  | 
| 1832 |  |  |  |  |  |  | sub _macro_define_generator { | 
| 1833 | 9 |  |  | 9 |  | 27 | my ( $self, @args ) = @_;	# $self if Macro object | 
| 1834 | 9 |  |  |  |  | 14 | my $output; | 
| 1835 | 9 |  |  |  |  | 20 | foreach my $macro ( @args ) { | 
| 1836 | 9 | 50 |  |  |  | 35 | if ( my $comp = $self->completion() ) { | 
| 1837 | 0 |  |  |  |  | 0 | $output .= "macro define \\\n    " . | 
| 1838 |  |  |  |  |  |  | "--completion '@$comp' \\\n    " . | 
| 1839 |  |  |  |  |  |  | "$macro \\\n    "; | 
| 1840 |  |  |  |  |  |  | } else { | 
| 1841 | 9 |  |  |  |  | 31 | $output .= "macro define $macro \\\n    "; | 
| 1842 |  |  |  |  |  |  | } | 
| 1843 | 9 |  |  |  |  | 30 | $output .= join( " \\\n    ", map { quoter( $_ ) } $self->def() ) . | 
|  | 17 |  |  |  |  | 45 |  | 
| 1844 |  |  |  |  |  |  | "\n"; | 
| 1845 |  |  |  |  |  |  | } | 
| 1846 | 9 |  |  |  |  | 37 | return $output; | 
| 1847 |  |  |  |  |  |  | } | 
| 1848 |  |  |  |  |  |  |  | 
| 1849 |  |  |  |  |  |  | sub _macro_sub_delete : Verb() Tweak( -completion _macro_list_complete ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1850 | 1 |  |  | 1 |  | 13 | my ( $self, undef, @args ) = __arguments( @_ ); | 
| 1851 | 1 |  |  |  |  | 17 | my $output; | 
| 1852 | 1 | 50 |  |  |  | 14 | foreach my $name (@args ? @args : keys %{$self->{macro}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 1853 | 1 |  |  |  |  | 27 | delete $self->{macro}{$name}; | 
| 1854 |  |  |  |  |  |  | } | 
| 1855 | 1 |  |  |  |  | 8 | return $output; | 
| 1856 | 20 |  |  | 20 |  | 9185 | } | 
|  | 20 |  |  |  |  | 62 |  | 
|  | 20 |  |  |  |  | 104 |  | 
| 1857 |  |  |  |  |  |  |  | 
| 1858 |  |  |  |  |  |  | sub _macro_sub_list : Verb() Tweak( -completion _macro_list_complete ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1859 | 9 |  |  | 9 |  | 28 | my ( $self, undef, @args ) = __arguments( @_ ); | 
| 1860 | 9 |  |  |  |  | 26 | my $output; | 
| 1861 | 9 | 100 |  |  |  | 35 | foreach my $name (sort @args ? @args : keys %{$self->{macro}}) { | 
|  | 1 |  |  |  |  | 12 |  | 
| 1862 | 9 | 50 |  |  |  | 37 | $self->{macro}{$name} | 
| 1863 |  |  |  |  |  |  | or next; | 
| 1864 | 9 |  |  |  |  | 56 | $output .= $self->{macro}{$name}->generator( $name ); | 
| 1865 |  |  |  |  |  |  | } | 
| 1866 | 9 |  |  |  |  | 29 | return $output; | 
| 1867 | 20 |  |  | 20 |  | 7015 | } | 
|  | 20 |  |  |  |  | 47 |  | 
|  | 20 |  |  |  |  | 102 |  | 
| 1868 |  |  |  |  |  |  |  | 
| 1869 |  |  |  |  |  |  | sub _macro_sub_load : Verb( lib=s verbose! ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 1870 | 0 |  |  | 0 |  | 0 | my ( $self, $opt, $name, @args ) = __arguments( @_ ); | 
| 1871 | 0 |  |  |  |  | 0 | my $output; | 
| 1872 | 0 | 0 |  |  |  | 0 | defined $name | 
| 1873 |  |  |  |  |  |  | or $self->wail( 'Must provide name of macro to load' ); | 
| 1874 |  |  |  |  |  |  | my %marg = ( | 
| 1875 |  |  |  |  |  |  | name	=> $name, | 
| 1876 |  |  |  |  |  |  | parent	=> $self, | 
| 1877 |  |  |  |  |  |  | generate	=> \&_macro_load_generator, | 
| 1878 |  |  |  |  |  |  | warner	=> $self->{_warner}, | 
| 1879 | 0 |  |  |  |  | 0 | ); | 
| 1880 |  |  |  |  |  |  | exists $opt->{lib} | 
| 1881 | 0 | 0 |  |  |  | 0 | and $marg{lib} = $opt->{lib}; | 
| 1882 | 0 |  | 0 |  |  | 0 | my $obj = $self->{_macro_load}{$name} ||= | 
| 1883 |  |  |  |  |  |  | Astro::App::Satpass2::Macro::Code->new( %marg ); | 
| 1884 | 0 | 0 |  |  |  | 0 | foreach my $mn ( @args ? @args : $obj->implements() ) { | 
| 1885 |  |  |  |  |  |  | $obj->implements( $mn, required => 1 ) | 
| 1886 | 0 | 0 |  |  |  | 0 | and $self->{macro}{$mn} = $obj; | 
| 1887 |  |  |  |  |  |  | } | 
| 1888 | 0 | 0 |  |  |  | 0 | if ( $opt->{verbose} ) { | 
| 1889 | 0 |  |  |  |  | 0 | ( my $fn = "$name.pm" ) =~ s| :: |/|smxg; | 
| 1890 | 0 |  |  |  |  | 0 | $output .= "Macro $name\n    loaded from $INC{$fn}\n"; | 
| 1891 | 0 |  |  |  |  | 0 | $output .= "    implements:\n"; | 
| 1892 | 0 |  |  |  |  | 0 | $output .= "        $_\n" for sort $obj->implements(); | 
| 1893 |  |  |  |  |  |  | } | 
| 1894 | 0 | 0 |  |  |  | 0 | $obj->implements( 'after_load', required => 0 ) | 
| 1895 |  |  |  |  |  |  | and $output .= $self->dispatch( after_load => $opt, $name, @args ); | 
| 1896 | 0 |  |  |  |  | 0 | return $output; | 
| 1897 | 20 |  |  | 20 |  | 10697 | } | 
|  | 20 |  |  |  |  | 75 |  | 
|  | 20 |  |  |  |  | 109 |  | 
| 1898 |  |  |  |  |  |  |  | 
| 1899 |  |  |  |  |  |  | sub _macro_load_generator { | 
| 1900 | 0 |  |  | 0 |  | 0 | my ( $self, @args ) = @_; | 
| 1901 | 0 |  |  |  |  | 0 | my @preamble = qw{ macro load }; | 
| 1902 | 0 | 0 |  |  |  | 0 | if ( $self->has_lib() ) { | 
| 1903 | 0 |  |  |  |  | 0 | push @preamble, '-lib', $self->lib(); | 
| 1904 | 0 | 0 |  |  |  | 0 | $self->relative() | 
| 1905 |  |  |  |  |  |  | and push @preamble, '-relative'; | 
| 1906 |  |  |  |  |  |  | } | 
| 1907 | 0 |  |  |  |  | 0 | push @preamble, $self->name(); | 
| 1908 | 0 |  |  |  |  | 0 | my $output; | 
| 1909 | 0 |  |  |  |  | 0 | foreach my $macro ( @args ) { | 
| 1910 | 0 |  |  |  |  | 0 | $output .= quoter( @preamble, $macro ) . "\n"; | 
| 1911 |  |  |  |  |  |  | } | 
| 1912 | 0 |  |  |  |  | 0 | return $output; | 
| 1913 |  |  |  |  |  |  | } | 
| 1914 |  |  |  |  |  |  |  | 
| 1915 |  |  |  |  |  |  | sub magnitude_table : Verb( name! reload! ) { | 
| 1916 | 0 |  |  | 0 | 1 | 0 | my ( undef, undef, @args ) = __arguments( @_ );	# Invocant, $opt unused | 
| 1917 |  |  |  |  |  |  |  | 
| 1918 | 0 | 0 |  |  |  | 0 | @args or @args = qw{show}; | 
| 1919 |  |  |  |  |  |  |  | 
| 1920 | 0 |  | 0 |  |  | 0 | my $verb = lc (shift (@args) || 'show'); | 
| 1921 |  |  |  |  |  |  |  | 
| 1922 | 0 |  |  |  |  | 0 | my $output; | 
| 1923 |  |  |  |  |  |  |  | 
| 1924 | 0 | 0 | 0 |  |  | 0 | if ( $verb eq 'show' || $verb eq 'list' ) { | 
| 1925 |  |  |  |  |  |  |  | 
| 1926 | 0 |  |  |  |  | 0 | my %data = Astro::Coord::ECI::TLE->magnitude_table( 'show', @args ); | 
| 1927 |  |  |  |  |  |  |  | 
| 1928 | 0 |  |  |  |  | 0 | foreach my $oid ( sort keys %data ) { | 
| 1929 | 0 |  |  |  |  | 0 | $output .= quoter( 'status', 'add', $oid, $data{$oid} ) | 
| 1930 |  |  |  |  |  |  | . "\n"; | 
| 1931 |  |  |  |  |  |  | } | 
| 1932 |  |  |  |  |  |  |  | 
| 1933 |  |  |  |  |  |  | } else { | 
| 1934 | 0 |  |  |  |  | 0 | Astro::Coord::ECI::TLE->magnitude_table( $verb, @args ); | 
| 1935 |  |  |  |  |  |  | } | 
| 1936 |  |  |  |  |  |  |  | 
| 1937 | 0 |  |  |  |  | 0 | return $output; | 
| 1938 |  |  |  |  |  |  |  | 
| 1939 | 20 |  |  | 20 |  | 10394 | } | 
|  | 20 |  |  |  |  | 48 |  | 
|  | 20 |  |  |  |  | 87 |  | 
| 1940 |  |  |  |  |  |  |  | 
| 1941 |  |  |  |  |  |  | # Attributes must all be on one line to process correctly under Perl | 
| 1942 |  |  |  |  |  |  | # 5.8.8. | 
| 1943 |  |  |  |  |  |  | sub pass : Verb( :compute __pass_options ) { | 
| 1944 | 20 |  |  | 20 | 1 | 138 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 1945 |  |  |  |  |  |  |  | 
| 1946 |  |  |  |  |  |  | $opt->{ephemeris} | 
| 1947 | 20 | 100 |  |  |  | 153 | and $opt->{almanac} = 1; | 
| 1948 |  |  |  |  |  |  | $opt->{almanac} | 
| 1949 |  |  |  |  |  |  | and not defined $opt->{ephemeris} | 
| 1950 |  |  |  |  |  |  | and $opt->{ephemeris} = { | 
| 1951 |  |  |  |  |  |  | pass_ics	=> 1, | 
| 1952 | 20 | 100 | 100 |  |  | 172 | }->{$opt->{_template}}; | 
| 1953 |  |  |  |  |  |  |  | 
| 1954 | 20 |  |  |  |  | 199 | $self->_apply_boolean_default( | 
| 1955 |  |  |  |  |  |  | $opt, 0, qw{ horizon illumination transit appulse } ); | 
| 1956 | 20 |  |  |  |  | 495 | $self->_apply_boolean_default( $opt, 0, qw{ am pm } ); | 
| 1957 | 20 | 50 | 66 |  |  | 226 | $opt->{am} or $opt->{pm} or $opt->{am} = $opt->{pm} = 1; | 
| 1958 | 20 |  |  |  |  | 126 | my $pass_start = $self->__parse_time ( | 
| 1959 |  |  |  |  |  |  | shift @args, $self->_get_day_noon()); | 
| 1960 | 20 |  | 100 |  |  | 151 | my $pass_end = $self->__parse_time (shift @args || '+7'); | 
| 1961 | 20 | 50 |  |  |  | 138 | $pass_start >= $pass_end | 
| 1962 |  |  |  |  |  |  | and $self->wail( 'End time must be after start time' ); | 
| 1963 |  |  |  |  |  |  |  | 
| 1964 | 20 |  |  |  |  | 124 | my $sta = $self->station(); | 
| 1965 |  |  |  |  |  |  | my @bodies = $self->__choose( $opt->{choose}, $self->{bodies} ) | 
| 1966 | 20 | 50 |  |  |  | 9199 | or $self->wail( 'No bodies selected' ); | 
| 1967 | 20 |  | 50 |  |  | 160 | my $pass_step = shift @args || 60; | 
| 1968 |  |  |  |  |  |  |  | 
| 1969 |  |  |  |  |  |  | #	Decide which model to use. | 
| 1970 |  |  |  |  |  |  |  | 
| 1971 | 20 |  |  |  |  | 85 | my $model = $self->{model}; | 
| 1972 |  |  |  |  |  |  |  | 
| 1973 |  |  |  |  |  |  | # Set the station for the objects in the sky. | 
| 1974 |  |  |  |  |  |  |  | 
| 1975 | 20 |  |  |  |  | 52 | foreach my $body ( @{ $self->{sky} } ) { | 
|  | 20 |  |  |  |  | 94 |  | 
| 1976 | 41 |  |  |  |  | 2574 | $body->set( station => $sta ); | 
| 1977 |  |  |  |  |  |  | } | 
| 1978 |  |  |  |  |  |  |  | 
| 1979 |  |  |  |  |  |  | #	Pick up horizon and appulse distance. | 
| 1980 |  |  |  |  |  |  |  | 
| 1981 | 20 |  |  |  |  | 1289 | my $horizon = deg2rad ($self->{horizon}); | 
| 1982 | 20 |  |  |  |  | 129 | my $appulse = deg2rad ($self->{appulse}); | 
| 1983 | 20 |  |  |  |  | 197 | my $pass_threshold = deg2rad( $self->{pass_threshold} ); | 
| 1984 |  |  |  |  |  |  |  | 
| 1985 |  |  |  |  |  |  | # In order that the interface not be completely rude, the interface | 
| 1986 |  |  |  |  |  |  | # allows -brightest to specify that you want the 'brightest' event. | 
| 1987 |  |  |  |  |  |  | # But this is controlled by the pass_variant attribute. So if | 
| 1988 |  |  |  |  |  |  | # -brightest appears, the pass_variant from it; otherwise we default | 
| 1989 |  |  |  |  |  |  | # -brightest from the pass_variant attribute.  We localize the | 
| 1990 |  |  |  |  |  |  | # pass_variant attribute before modifying it, since the -brightest | 
| 1991 |  |  |  |  |  |  | # option is to hold for this call only. We modify it (rather than | 
| 1992 |  |  |  |  |  |  | # just passing a local copy to the bodies) because | 
| 1993 |  |  |  |  |  |  | # Formatter::Template needs to know what it is, and modifying this | 
| 1994 |  |  |  |  |  |  | # object is the obvious way to pass the information. | 
| 1995 | 20 |  |  |  |  | 127 | local $self->{pass_variant} = $self->{pass_variant}; | 
| 1996 | 20 | 50 |  |  |  | 94 | if ( $opt->{brightest} ) { | 
|  |  | 50 |  |  |  |  |  | 
| 1997 | 0 |  |  |  |  | 0 | $self->{pass_variant} |= PASS_VARIANT_BRIGHTEST; | 
| 1998 |  |  |  |  |  |  | } elsif ( exists $opt->{brightest} ) { | 
| 1999 | 0 |  |  |  |  | 0 | $self->{pass_variant} &= ~ PASS_VARIANT_BRIGHTEST; | 
| 2000 |  |  |  |  |  |  | } else { | 
| 2001 | 20 |  |  |  |  | 113 | $opt->{brightest} = $self->{pass_variant} & PASS_VARIANT_BRIGHTEST; | 
| 2002 |  |  |  |  |  |  | } | 
| 2003 | 20 |  |  |  |  | 65 | my $pass_variant = $self->{pass_variant}; | 
| 2004 |  |  |  |  |  |  |  | 
| 2005 |  |  |  |  |  |  | #	Foreach body to be modelled | 
| 2006 |  |  |  |  |  |  |  | 
| 2007 | 20 |  |  |  |  | 48 | my @accumulate;	# For chronological output. | 
| 2008 | 20 |  |  |  |  | 100 | foreach my $tle ( $self->_aggregate( \@bodies ) ) { | 
| 2009 |  |  |  |  |  |  |  | 
| 2010 |  |  |  |  |  |  | { | 
| 2011 | 39 | 50 |  |  |  | 2864 | my $mdl = $tle->get('inertial') ? $model : | 
|  | 39 |  |  |  |  | 149 |  | 
| 2012 |  |  |  |  |  |  | $tle->get('model'); | 
| 2013 |  |  |  |  |  |  | $tle->set ( | 
| 2014 |  |  |  |  |  |  | appulse => $appulse, | 
| 2015 |  |  |  |  |  |  | backdate => $self->{backdate}, | 
| 2016 |  |  |  |  |  |  | debug => $self->{debug}, | 
| 2017 |  |  |  |  |  |  | edge_of_earths_shadow => $self->{edge_of_earths_shadow}, | 
| 2018 |  |  |  |  |  |  | geometric => $self->{geometric}, | 
| 2019 |  |  |  |  |  |  | horizon => $horizon, | 
| 2020 |  |  |  |  |  |  | interval => ( $self->{verbose} ? $pass_step : 0 ), | 
| 2021 |  |  |  |  |  |  | model => $mdl, | 
| 2022 |  |  |  |  |  |  | pass_threshold => $pass_threshold, | 
| 2023 |  |  |  |  |  |  | pass_variant	=> $pass_variant, | 
| 2024 |  |  |  |  |  |  | station	=> $sta, | 
| 2025 |  |  |  |  |  |  | twilight => $self->{_twilight}, | 
| 2026 |  |  |  |  |  |  | visible => $self->{visible}, | 
| 2027 | 39 | 50 |  |  |  | 1658 | ); | 
| 2028 |  |  |  |  |  |  | } | 
| 2029 |  |  |  |  |  |  |  | 
| 2030 |  |  |  |  |  |  | eval { | 
| 2031 |  |  |  |  |  |  | push @accumulate, $self->_pass_select_event( $opt, $tle->pass ( | 
| 2032 | 39 |  |  |  |  | 331 | $pass_start, $pass_end, $self->{sky} ) ); | 
| 2033 | 39 |  |  |  |  | 297 | 1; | 
| 2034 | 39 | 50 |  |  |  | 15151 | } or do { | 
| 2035 | 0 | 0 |  |  |  | 0 | $@ =~ m/ \Q$interrupted\E /smxo and $self->wail($@); | 
| 2036 | 0 | 0 |  |  |  | 0 | $opt->{quiet} or $self->whinge($@); | 
| 2037 |  |  |  |  |  |  | }; | 
| 2038 |  |  |  |  |  |  | } | 
| 2039 |  |  |  |  |  |  |  | 
| 2040 | 20 |  |  |  |  | 148 | @accumulate = $self->__pass_filter_am_pm( $opt, @accumulate ); | 
| 2041 |  |  |  |  |  |  |  | 
| 2042 |  |  |  |  |  |  | $opt->{chronological} | 
| 2043 | 20 | 100 |  |  |  | 110 | and @accumulate = sort { $a->{time} <=> $b->{time} } | 
|  | 0 |  |  |  |  | 0 |  | 
| 2044 |  |  |  |  |  |  | @accumulate; | 
| 2045 |  |  |  |  |  |  |  | 
| 2046 |  |  |  |  |  |  | # Record number of events found. | 
| 2047 |  |  |  |  |  |  | # NOTE that in this case an event is an entire pass. | 
| 2048 |  |  |  |  |  |  |  | 
| 2049 | 20 |  |  |  |  | 97 | $self->{events} += @accumulate; | 
| 2050 |  |  |  |  |  |  |  | 
| 2051 | 20 | 100 |  |  |  | 95 | if ( $opt->{almanac} ) { | 
| 2052 | 4 |  |  |  |  | 16 | my %almanac; | 
| 2053 | 4 |  |  |  |  | 19 | foreach my $pass ( @accumulate ) { | 
| 2054 | 6 |  |  |  |  | 49 | my $illum = $pass->{body}->get( 'illum' ); | 
| 2055 | 6 |  |  |  |  | 126 | my $noon = $self->_get_day_noon( $pass->{time} ); | 
| 2056 | 6 |  | 33 |  |  | 517 | $almanac{$noon}{$illum} ||= do { | 
| 2057 | 6 |  |  |  |  | 25 | my @day; | 
| 2058 |  |  |  |  |  |  |  | 
| 2059 |  |  |  |  |  |  | my @events = grep { { | 
| 2060 |  |  |  |  |  |  | horizon		=> 1, | 
| 2061 |  |  |  |  |  |  | twilight	=> 1, | 
| 2062 |  |  |  |  |  |  | }->{$_->{almanac}{event}} | 
| 2063 | 36 |  |  |  |  | 1070947 | } $illum->almanac_hash( | 
| 2064 | 6 |  |  |  |  | 59 | $self->_get_day_midnight( $pass->{time} ) ); | 
| 2065 |  |  |  |  |  |  |  | 
| 2066 | 6 |  |  |  |  | 87 | _almanac_localize( @events ); | 
| 2067 |  |  |  |  |  |  |  | 
| 2068 | 6 |  |  |  |  | 27 | foreach my $evt ( @events ) { | 
| 2069 | 24 |  |  |  |  | 49 | $evt->{event} = 'almanac'; | 
| 2070 | 24 | 100 |  |  |  | 72 | my $pm = $evt->{time} >= $noon ? 1 : 0; | 
| 2071 | 24 |  |  |  |  | 39 | push @{ $day[$pm] }, $evt; | 
|  | 24 |  |  |  |  | 70 |  | 
| 2072 |  |  |  |  |  |  | } | 
| 2073 |  |  |  |  |  |  |  | 
| 2074 | 6 |  |  |  |  | 58 | \@day; | 
| 2075 |  |  |  |  |  |  | }; | 
| 2076 |  |  |  |  |  |  |  | 
| 2077 | 6 | 50 |  |  |  | 49 | $pass->{_pm} = my $pm = $pass->{time} >= $noon ? 1 : 0; | 
| 2078 |  |  |  |  |  |  | # TODO this way ALL passes get the almanac events. Is this | 
| 2079 |  |  |  |  |  |  | # what I want? It varies. For --ics it is. For --events it | 
| 2080 |  |  |  |  |  |  | # is not. For neither it's probably not. | 
| 2081 | 6 | 100 |  |  |  | 69 | if ( $opt->{ephemeris} ) { | 
| 2082 | 3 |  |  |  |  | 59 | @{ $pass->{events} } = sort { $a->{time} <=> $b->{time} | 
| 2083 | 3 |  |  |  |  | 9 | } @{ $pass->{events} }, @{ $almanac{$noon}{$illum}[$pm] }; | 
|  | 26 |  |  |  |  | 59 |  | 
|  | 3 |  |  |  |  | 11 |  | 
|  | 3 |  |  |  |  | 33 |  | 
| 2084 |  |  |  |  |  |  | } | 
| 2085 |  |  |  |  |  |  | } | 
| 2086 |  |  |  |  |  |  |  | 
| 2087 | 4 | 100 |  |  |  | 41 | unless( $opt->{ephemeris} ) { | 
| 2088 | 2 |  |  |  |  | 9 | foreach my $pass ( @accumulate ) { | 
| 2089 |  |  |  |  |  |  | $pass->{_pm} | 
| 2090 | 3 | 50 |  |  |  | 17 | or next; | 
| 2091 | 0 |  |  |  |  | 0 | my $illum = $pass->{body}->get( 'illum' ); | 
| 2092 | 0 |  |  |  |  | 0 | my $noon = $self->_get_day_noon( $pass->{time} ); | 
| 2093 | 0 | 0 |  |  |  | 0 | $almanac{$noon}{$illum}[1] | 
| 2094 |  |  |  |  |  |  | or next; | 
| 2095 | 0 |  |  |  |  | 0 | @{ $pass->{events} } = sort { $a->{time} <=> $b->{time} } | 
|  | 0 |  |  |  |  | 0 |  | 
| 2096 | 0 |  |  |  |  | 0 | @{ $pass->{events} }, | 
| 2097 | 0 |  |  |  |  | 0 | @{ $almanac{$noon}{$illum}[1] }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2098 | 0 |  |  |  |  | 0 | $almanac{$noon}{$illum}[1] = undef; | 
| 2099 |  |  |  |  |  |  | } | 
| 2100 | 2 |  |  |  |  | 8 | foreach my $pass ( reverse @accumulate ) { | 
| 2101 |  |  |  |  |  |  | $pass->{_pm} | 
| 2102 | 3 | 50 |  |  |  | 16 | and next; | 
| 2103 | 3 |  |  |  |  | 23 | my $illum = $pass->{body}->get( 'illum' ); | 
| 2104 | 3 |  |  |  |  | 73 | my $noon = $self->_get_day_noon( $pass->{time} ); | 
| 2105 | 3 | 50 |  |  |  | 199 | $almanac{$noon}{$illum}[0] | 
| 2106 |  |  |  |  |  |  | or next; | 
| 2107 | 3 |  |  |  |  | 15 | @{ $pass->{events} } = sort { $a->{time} <=> $b->{time} } | 
|  | 26 |  |  |  |  | 55 |  | 
| 2108 | 3 |  |  |  |  | 13 | @{ $pass->{events} }, | 
| 2109 | 3 |  |  |  |  | 9 | @{ $almanac{$noon}{$illum}[0] }; | 
|  | 3 |  |  |  |  | 25 |  | 
| 2110 | 3 |  |  |  |  | 43 | $almanac{$noon}{$illum}[0] = undef; | 
| 2111 |  |  |  |  |  |  | } | 
| 2112 |  |  |  |  |  |  | } | 
| 2113 |  |  |  |  |  |  | } | 
| 2114 |  |  |  |  |  |  |  | 
| 2115 |  |  |  |  |  |  | return $self->__format_data( | 
| 2116 | 20 |  |  |  |  | 146 | $opt->{_template} => \@accumulate, $opt ); | 
| 2117 |  |  |  |  |  |  |  | 
| 2118 | 20 |  |  | 20 |  | 26667 | } | 
|  | 20 |  |  |  |  | 59 |  | 
|  | 20 |  |  |  |  | 94 |  | 
| 2119 |  |  |  |  |  |  |  | 
| 2120 |  |  |  |  |  |  | sub __pass_filter_am_pm { | 
| 2121 | 20 |  |  | 20 |  | 97 | my ( $self, $opt, @accumulate ) = @_; | 
| 2122 | 20 |  | 50 |  |  | 81 | $opt ||= {}; | 
| 2123 |  |  |  |  |  |  | $opt->{am} xor $opt->{pm} | 
| 2124 | 20 | 100 | 75 |  |  | 243 | or return @accumulate; | 
| 2125 |  |  |  |  |  |  | return ( | 
| 2126 | 6 |  |  |  |  | 34 | map { $_->[0] } | 
| 2127 | 12 |  | 50 |  |  | 223 | grep { $opt->{am} xor $_->[1] } | 
| 2128 | 2 |  |  |  |  | 9 | map { [ | 
| 2129 |  |  |  |  |  |  | $_, | 
| 2130 |  |  |  |  |  |  | $_->{time} >= $self->_get_day_noon( $_->{time} ) | 
| 2131 | 12 |  |  |  |  | 457 | ] } @accumulate | 
| 2132 |  |  |  |  |  |  | ); | 
| 2133 |  |  |  |  |  |  | } | 
| 2134 |  |  |  |  |  |  |  | 
| 2135 |  |  |  |  |  |  | sub __pass_options { | 
| 2136 | 20 |  |  | 20 |  | 73 | my ( $self, $opt ) = @_; | 
| 2137 |  |  |  |  |  |  | return [ | 
| 2138 | 20 |  |  |  |  | 138 | qw{ | 
| 2139 |  |  |  |  |  |  | almanac! am! appulse! brightest|magnitude! choose=s@ | 
| 2140 |  |  |  |  |  |  | chronological! ephemeris! dump! horizon|rise|set! | 
| 2141 |  |  |  |  |  |  | illumination! pm! | 
| 2142 |  |  |  |  |  |  | quiet! transit|maximum|culmination! | 
| 2143 |  |  |  |  |  |  | }, | 
| 2144 |  |  |  |  |  |  | $self->_templates_to_options( pass => $opt ), | 
| 2145 |  |  |  |  |  |  | ]; | 
| 2146 |  |  |  |  |  |  | } | 
| 2147 |  |  |  |  |  |  |  | 
| 2148 |  |  |  |  |  |  | { | 
| 2149 |  |  |  |  |  |  | my @selector; | 
| 2150 |  |  |  |  |  |  | $selector[ PASS_EVENT_SHADOWED ]	= 'illumination'; | 
| 2151 |  |  |  |  |  |  | $selector[ PASS_EVENT_LIT ]		= 'illumination'; | 
| 2152 |  |  |  |  |  |  | $selector[ PASS_EVENT_DAY ]		= 'illumination'; | 
| 2153 |  |  |  |  |  |  | $selector[ PASS_EVENT_RISE ]	= 'horizon'; | 
| 2154 |  |  |  |  |  |  | $selector[ PASS_EVENT_MAX ]		= 'transit'; | 
| 2155 |  |  |  |  |  |  | $selector[ PASS_EVENT_SET ]		= 'horizon'; | 
| 2156 |  |  |  |  |  |  | $selector[ PASS_EVENT_APPULSE ]	= 'appulse'; | 
| 2157 |  |  |  |  |  |  | $selector[ PASS_EVENT_START ]	= 'horizon'; | 
| 2158 |  |  |  |  |  |  | $selector[ PASS_EVENT_END ]		= 'horizon'; | 
| 2159 |  |  |  |  |  |  | $selector[ PASS_EVENT_BRIGHTEST ]	= 'brightest'; | 
| 2160 |  |  |  |  |  |  |  | 
| 2161 |  |  |  |  |  |  | # Remove from the pass data any events that are not wanted. The | 
| 2162 |  |  |  |  |  |  | # arguments are $self, the $opt hash reference that (among other | 
| 2163 |  |  |  |  |  |  | # things) specifies the desired events, and the passes, each pass | 
| 2164 |  |  |  |  |  |  | # being an argument. The modified passes are returned. | 
| 2165 |  |  |  |  |  |  | sub _pass_select_event { | 
| 2166 | 39 |  |  | 39 |  | 56228070 | my ( undef, $opt, @passes ) = @_;	# Invocant unused | 
| 2167 | 39 |  |  |  |  | 131 | my @rslt; | 
| 2168 | 39 |  |  |  |  | 149 | foreach my $pass ( @passes ) { | 
| 2169 | 38 |  |  |  |  | 238 | @{ $pass->{events} } = grep { | 
| 2170 |  |  |  |  |  |  | _pass_select_event_code( $opt, $_->{event} ) | 
| 2171 | 38 | 50 |  |  |  | 96 | } @{ $pass->{events} } | 
|  | 136 |  |  |  |  | 435 |  | 
|  | 38 |  |  |  |  | 186 |  | 
| 2172 |  |  |  |  |  |  | and push @rslt, $pass; | 
| 2173 |  |  |  |  |  |  | } | 
| 2174 |  |  |  |  |  |  | return @rslt | 
| 2175 | 39 |  |  |  |  | 152 | } | 
| 2176 |  |  |  |  |  |  |  | 
| 2177 |  |  |  |  |  |  | # Determine whether an event is to be reported for the pass. The | 
| 2178 |  |  |  |  |  |  | # arguments are the $opt hash reference and the event code or name. | 
| 2179 |  |  |  |  |  |  | # Anything that is not a dualvar and not an integer is accepted, on | 
| 2180 |  |  |  |  |  |  | # the presumption that it is an ad-hoc event provided by some | 
| 2181 |  |  |  |  |  |  | # subclass. The null event is always accepted on the presumption | 
| 2182 |  |  |  |  |  |  | # that if the user did not want it he or she would not have asked | 
| 2183 |  |  |  |  |  |  | # for it. Anything that is left is accepted or rejected based on the | 
| 2184 |  |  |  |  |  |  | # option hash and the @selector array (defined above). | 
| 2185 |  |  |  |  |  |  | sub _pass_select_event_code { | 
| 2186 | 136 |  |  | 136 |  | 308 | my ( $opt, $event ) = @_; | 
| 2187 | 136 | 50 | 33 |  |  | 467 | isdual( $event ) | 
| 2188 |  |  |  |  |  |  | or $event !~ m/ \D /smx | 
| 2189 |  |  |  |  |  |  | or return 1; | 
| 2190 | 136 | 50 |  |  |  | 293 | $event == PASS_EVENT_NONE | 
| 2191 |  |  |  |  |  |  | and return 1; | 
| 2192 | 136 |  | 66 |  |  | 830 | return defined $selector[ $event ] && $opt->{ $selector[ $event ] }; | 
| 2193 |  |  |  |  |  |  | } | 
| 2194 |  |  |  |  |  |  | } | 
| 2195 |  |  |  |  |  |  |  | 
| 2196 |  |  |  |  |  |  | sub perl : Tokenize( -noexpand_tilde ) : Verb( eval! setup! ) { | 
| 2197 | 2 |  |  | 2 | 1 | 22 | my ( $self, $opt, $file, @args ) = __arguments( @_ ); | 
| 2198 | 2 | 50 |  |  |  | 23 | defined $file | 
| 2199 |  |  |  |  |  |  | or $self->wail( 'At least one argument is required' ); | 
| 2200 |  |  |  |  |  |  | $opt->{setup} | 
| 2201 | 2 | 50 | 0 |  |  | 30 | and push @{ $self->{_perl} ||= [] }, [ $opt, $file, @args ]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2202 | 2 |  |  |  |  | 25 | local @ARGV = ( $self, map { $self->expand_tilde( $_ ) } @args ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 2203 |  |  |  |  |  |  | $opt->{eval} | 
| 2204 | 2 | 100 |  |  |  | 41 | or local $0 = $self->expand_tilde( $file ); | 
| 2205 |  |  |  |  |  |  |  | 
| 2206 |  |  |  |  |  |  | my $data = $opt->{eval} ? | 
| 2207 | 2 | 100 |  |  |  | 53 | $file : | 
| 2208 |  |  |  |  |  |  | $self->_file_reader( $file, { glob => 1 } ); | 
| 2209 | 2 |  |  |  |  | 247 | my $rslt = eval $data; ## no critic (BuiltinFunctions::ProhibitStringyEval) | 
| 2210 | 2 | 100 |  |  |  | 48 | $@ | 
| 2211 |  |  |  |  |  |  | and $self->wail( "Failed to eval '$file': $@" ); | 
| 2212 | 1 | 50 |  |  |  | 12 | instance( $rslt, 'Astro::App::Satpass2' ) | 
| 2213 |  |  |  |  |  |  | or return $rslt; | 
| 2214 | 0 |  |  |  |  | 0 | return; | 
| 2215 | 20 |  |  | 20 |  | 18118 | } | 
|  | 20 |  |  |  |  | 41 |  | 
|  | 20 |  |  |  |  | 91 |  | 
| 2216 |  |  |  |  |  |  |  | 
| 2217 |  |  |  |  |  |  | sub phase : Verb( choose=s@ ) { | 
| 2218 | 1 |  |  | 1 | 1 | 9 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 2219 |  |  |  |  |  |  |  | 
| 2220 | 1 |  |  |  |  | 10 | my $time = $self->__parse_time (shift @args, time ); | 
| 2221 |  |  |  |  |  |  |  | 
| 2222 |  |  |  |  |  |  | my @sky = $self->__choose( $opt->{choose}, $self->{sky} ) | 
| 2223 | 1 | 50 |  |  |  | 9 | or $self->wail( 'No bodies selected' ); | 
| 2224 |  |  |  |  |  |  | return $self->__format_data( | 
| 2225 |  |  |  |  |  |  | phase => [ | 
| 2226 | 1 |  |  |  |  | 6 | map { { body => $_->universal( $time ), time => $time } } | 
| 2227 | 1 |  |  |  |  | 4 | grep { $_->can( 'phase' ) } | 
|  | 2 |  |  |  |  | 20 |  | 
| 2228 |  |  |  |  |  |  | @sky | 
| 2229 |  |  |  |  |  |  | ], $opt ); | 
| 2230 | 20 |  |  | 20 |  | 7488 | } | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 99 |  | 
| 2231 |  |  |  |  |  |  |  | 
| 2232 |  |  |  |  |  |  | sub position : Verb( choose=s@ questionable|spare! quiet! ) { | 
| 2233 | 4 |  |  | 4 | 1 | 29296 | my ( $self, $opt, $time ) = __arguments( @_ ); | 
| 2234 |  |  |  |  |  |  |  | 
| 2235 | 4 | 50 |  |  |  | 33 | if ( defined $time ) { | 
| 2236 | 4 |  |  |  |  | 35 | $time = $self->__parse_time($time); | 
| 2237 |  |  |  |  |  |  | } else { | 
| 2238 | 0 |  |  |  |  | 0 | $time = time; | 
| 2239 |  |  |  |  |  |  | } | 
| 2240 |  |  |  |  |  |  |  | 
| 2241 |  |  |  |  |  |  | #	Define the observing station. | 
| 2242 |  |  |  |  |  |  |  | 
| 2243 | 4 |  |  |  |  | 30 | my $sta = $self->station(); | 
| 2244 | 4 |  |  |  |  | 1859 | $sta->universal( $time ); | 
| 2245 |  |  |  |  |  |  |  | 
| 2246 |  |  |  |  |  |  | my @list = $self->__choose( { bodies => 1, sky => 1 }, | 
| 2247 | 4 |  |  |  |  | 215 | $opt->{choose} ); | 
| 2248 |  |  |  |  |  |  |  | 
| 2249 | 4 |  |  |  |  | 13 | my @good; | 
| 2250 | 4 |  |  |  |  | 20 | my $horizon = deg2rad ($self->{horizon}); | 
| 2251 | 4 |  |  |  |  | 45 | foreach my $body (@list) { | 
| 2252 | 13 | 100 |  |  |  | 62 | if ( $body->represents( 'Astro::Coord::ECI::TLE' ) ) { | 
| 2253 |  |  |  |  |  |  | $body->set ( | 
| 2254 |  |  |  |  |  |  | backdate => $self->{backdate}, | 
| 2255 |  |  |  |  |  |  | debug => $self->{debug}, | 
| 2256 |  |  |  |  |  |  | edge_of_earths_shadow => $self->{edge_of_earths_shadow}, | 
| 2257 |  |  |  |  |  |  | geometric => $self->{geometric}, | 
| 2258 |  |  |  |  |  |  | horizon => $horizon, | 
| 2259 |  |  |  |  |  |  | station	=> $sta, | 
| 2260 |  |  |  |  |  |  | twilight => $self->{_twilight}, | 
| 2261 | 4 |  |  |  |  | 200 | ); | 
| 2262 |  |  |  |  |  |  | $body->get('inertial') | 
| 2263 | 4 | 50 |  |  |  | 983 | and $body->set( model => $self->{model} ); | 
| 2264 |  |  |  |  |  |  | } | 
| 2265 |  |  |  |  |  |  | eval { | 
| 2266 | 13 |  |  |  |  | 49 | $body->universal ($time); | 
| 2267 | 10 |  |  |  |  | 3844 | push @good, $body; | 
| 2268 | 10 |  |  |  |  | 46 | 1; | 
| 2269 | 13 | 100 |  |  |  | 414 | } or do { | 
| 2270 | 3 | 50 |  |  |  | 2715 | $@ =~ m/ \Q$interrupted\E /smxo and $self->wail($@); | 
| 2271 | 3 | 50 |  |  |  | 48 | $opt->{quiet} or $self->whinge($@); | 
| 2272 |  |  |  |  |  |  | }; | 
| 2273 |  |  |  |  |  |  | } | 
| 2274 |  |  |  |  |  |  |  | 
| 2275 |  |  |  |  |  |  | return $self->__format_data( | 
| 2276 |  |  |  |  |  |  | position => { | 
| 2277 |  |  |  |  |  |  | bodies		=> \@good, | 
| 2278 |  |  |  |  |  |  | questionable	=> $opt->{questionable}, | 
| 2279 |  |  |  |  |  |  | station		=> $self->station()->universal( | 
| 2280 |  |  |  |  |  |  | $time ), | 
| 2281 |  |  |  |  |  |  | time		=> $time, | 
| 2282 |  |  |  |  |  |  | twilight		=> $self->{_twilight}, | 
| 2283 | 4 |  |  |  |  | 34 | }, $opt ); | 
| 2284 | 20 |  |  | 20 |  | 11220 | } | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 120 |  | 
| 2285 |  |  |  |  |  |  |  | 
| 2286 |  |  |  |  |  |  | sub pwd : Verb() { | 
| 2287 | 1 |  |  | 1 | 1 | 6562 | return Cwd::cwd() . "\n"; | 
| 2288 | 20 |  |  | 20 |  | 4587 | } | 
|  | 20 |  |  |  |  | 46 |  | 
|  | 20 |  |  |  |  | 131 |  | 
| 2289 |  |  |  |  |  |  |  | 
| 2290 |  |  |  |  |  |  | { | 
| 2291 |  |  |  |  |  |  | my @quarter_name = map { "q$_" } 0 .. 3; | 
| 2292 |  |  |  |  |  |  |  | 
| 2293 |  |  |  |  |  |  | sub quarters : Verb( choose=s@ dump! q0|new|spring! q1|first|summer!  q2|full|fall q3|last|winter ) { | 
| 2294 | 1 |  |  | 1 | 1 | 41 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 2295 |  |  |  |  |  |  |  | 
| 2296 | 1 |  |  |  |  | 43 | my $start = $self->__parse_time ( | 
| 2297 |  |  |  |  |  |  | $args[0], $self->_get_day_midnight() ); | 
| 2298 | 1 |  | 50 |  |  | 37 | my $end = $self->__parse_time ($args[1] || '+30'); | 
| 2299 |  |  |  |  |  |  |  | 
| 2300 | 1 |  |  |  |  | 15 | $self->_apply_boolean_default( $opt, 0, map { "q$_" } 0 .. 3 ); | 
|  | 4 |  |  |  |  | 21 |  | 
| 2301 |  |  |  |  |  |  |  | 
| 2302 |  |  |  |  |  |  | my @sky = $self->__choose( $opt->{choose}, $self->{sky} ) | 
| 2303 | 1 | 50 |  |  |  | 31 | or $self->wail( 'No bodies selected' ); | 
| 2304 |  |  |  |  |  |  |  | 
| 2305 | 1 |  |  |  |  | 12 | my @almanac; | 
| 2306 |  |  |  |  |  |  |  | 
| 2307 |  |  |  |  |  |  | # Iterate over any background objects, accumulating all | 
| 2308 |  |  |  |  |  |  | # quarter-phases of each until we get one after the end time. We | 
| 2309 |  |  |  |  |  |  | # silently ignore bodies that do not support the next_quarter() | 
| 2310 |  |  |  |  |  |  | # method. | 
| 2311 |  |  |  |  |  |  |  | 
| 2312 | 1 |  |  |  |  | 10 | foreach my $body ( @sky ) { | 
| 2313 | 2 | 50 |  |  |  | 37 | next unless $body->can ('next_quarter_hash'); | 
| 2314 | 2 |  |  |  |  | 34 | $body->universal ($start); | 
| 2315 |  |  |  |  |  |  |  | 
| 2316 | 2 |  |  |  |  | 2594 | while (1) { | 
| 2317 | 7 |  |  |  |  | 69 | my $hash = $body->next_quarter_hash(); | 
| 2318 | 7 | 100 |  |  |  | 203871 | $hash->{time} > $end and last; | 
| 2319 | 5 | 50 |  |  |  | 39 | $opt->{$quarter_name[$hash->{almanac}{detail}]} | 
| 2320 |  |  |  |  |  |  | or next; | 
| 2321 | 5 |  |  |  |  | 15 | push @almanac, $hash; | 
| 2322 |  |  |  |  |  |  | } | 
| 2323 |  |  |  |  |  |  | } | 
| 2324 |  |  |  |  |  |  |  | 
| 2325 |  |  |  |  |  |  | # Localize the event descriptions if appropriate. | 
| 2326 |  |  |  |  |  |  |  | 
| 2327 | 1 |  |  |  |  | 5 | foreach my $event ( @almanac ) { | 
| 2328 |  |  |  |  |  |  | $event->{almanac}{description} = __localize( | 
| 2329 |  |  |  |  |  |  | text	=> [ almanac => $event->{body}->get( 'name' ), | 
| 2330 |  |  |  |  |  |  | $event->{almanac}{event}, $event->{almanac}{detail} | 
| 2331 |  |  |  |  |  |  | ], | 
| 2332 |  |  |  |  |  |  | default	=> $event->{almanac}{description}, | 
| 2333 |  |  |  |  |  |  | argument	=> $event->{body}, | 
| 2334 | 5 |  |  |  |  | 36 | ); | 
| 2335 |  |  |  |  |  |  | } | 
| 2336 |  |  |  |  |  |  |  | 
| 2337 |  |  |  |  |  |  | # Record number of events found | 
| 2338 |  |  |  |  |  |  |  | 
| 2339 | 1 |  |  |  |  | 16 | $self->{events} += @almanac; | 
| 2340 |  |  |  |  |  |  |  | 
| 2341 |  |  |  |  |  |  | # Sort and display the quarter-phase information. | 
| 2342 |  |  |  |  |  |  |  | 
| 2343 |  |  |  |  |  |  | return $self->__format_data( | 
| 2344 |  |  |  |  |  |  | almanac => [ | 
| 2345 | 1 |  |  |  |  | 28 | sort { $a->{time} <=> $b->{time} } | 
|  | 9 |  |  |  |  | 45 |  | 
| 2346 |  |  |  |  |  |  | @almanac | 
| 2347 |  |  |  |  |  |  | ], $opt ); | 
| 2348 |  |  |  |  |  |  |  | 
| 2349 | 20 |  |  | 20 |  | 12229 | } | 
|  | 20 |  |  |  |  | 48 |  | 
|  | 20 |  |  |  |  | 119 |  | 
| 2350 |  |  |  |  |  |  | } | 
| 2351 |  |  |  |  |  |  |  | 
| 2352 |  |  |  |  |  |  | { | 
| 2353 |  |  |  |  |  |  | my $go; | 
| 2354 |  |  |  |  |  |  |  | 
| 2355 |  |  |  |  |  |  | sub run { | 
| 2356 | 0 |  |  | 0 | 1 | 0 | my ( $self, @args ) = @_; | 
| 2357 |  |  |  |  |  |  |  | 
| 2358 |  |  |  |  |  |  | # We can be called statically. If we are, instantiate. | 
| 2359 | 0 | 0 |  |  |  | 0 | ref $self or $self = $self->new(warning => 1); | 
| 2360 |  |  |  |  |  |  |  | 
| 2361 |  |  |  |  |  |  | # Put all the I/O into UTF-8 mode. | 
| 2362 | 0 |  |  |  |  | 0 | binmode STDIN, ':encoding(UTF-8)'; | 
| 2363 | 0 |  |  |  |  | 0 | binmode STDOUT, DEFAULT_STDOUT_LAYERS; | 
| 2364 | 0 |  |  |  |  | 0 | binmode STDERR, ':encoding(UTF-8)'; | 
| 2365 |  |  |  |  |  |  |  | 
| 2366 |  |  |  |  |  |  | # If the undocumented first option is a code reference, use it to | 
| 2367 |  |  |  |  |  |  | # get input. | 
| 2368 | 0 |  |  |  |  | 0 | my $in; | 
| 2369 | 0 | 0 |  |  |  | 0 | CODE_REF eq ref $args[0] | 
| 2370 |  |  |  |  |  |  | and $in = shift @args; | 
| 2371 |  |  |  |  |  |  |  | 
| 2372 |  |  |  |  |  |  | # Parse the command options. -level1 is undocumented. | 
| 2373 | 0 |  |  |  |  | 0 | my %opt; | 
| 2374 | 0 |  | 0 |  |  | 0 | $go ||= Getopt::Long::Parser->new(); | 
| 2375 | 0 | 0 |  |  |  | 0 | $go->getoptionsfromarray( | 
| 2376 |  |  |  |  |  |  | \@args, | 
| 2377 |  |  |  |  |  |  | \%opt, | 
| 2378 |  |  |  |  |  |  | qw{ | 
| 2379 |  |  |  |  |  |  | echo! filter! gmt! help initialization_file|initfile=s | 
| 2380 |  |  |  |  |  |  | level1! version | 
| 2381 |  |  |  |  |  |  | }, | 
| 2382 |  |  |  |  |  |  | ) | 
| 2383 |  |  |  |  |  |  | or $self->wail( 'See the help method for valid options' ); | 
| 2384 |  |  |  |  |  |  |  | 
| 2385 |  |  |  |  |  |  | # If -version, do it and return. | 
| 2386 | 0 | 0 |  |  |  | 0 | if ( $opt{version} ) { | 
| 2387 | 0 |  |  |  |  | 0 | print $self->version(); | 
| 2388 | 0 |  |  |  |  | 0 | return; | 
| 2389 |  |  |  |  |  |  | } | 
| 2390 |  |  |  |  |  |  |  | 
| 2391 |  |  |  |  |  |  | # If -help, do it and return. | 
| 2392 | 0 | 0 |  |  |  | 0 | if ( $opt{help} ) { | 
| 2393 | 0 |  |  |  |  | 0 | $self->help(); | 
| 2394 | 0 |  |  |  |  | 0 | return; | 
| 2395 |  |  |  |  |  |  | } | 
| 2396 |  |  |  |  |  |  |  | 
| 2397 |  |  |  |  |  |  | # Get an input routine if we do not already have one. | 
| 2398 | 0 |  | 0 |  |  | 0 | $in ||= $self->_get_readline(); | 
| 2399 |  |  |  |  |  |  |  | 
| 2400 |  |  |  |  |  |  | # Some options get processed before we initialize. | 
| 2401 | 0 |  |  |  |  | 0 | foreach my $name ( qw{ echo filter } ) { | 
| 2402 |  |  |  |  |  |  | exists $opt{$name} | 
| 2403 | 0 | 0 |  |  |  | 0 | and $self->set( $name => delete( $opt{$name} ) ); | 
| 2404 |  |  |  |  |  |  | } | 
| 2405 |  |  |  |  |  |  |  | 
| 2406 |  |  |  |  |  |  | # Display the front matter if desired. | 
| 2407 | 0 | 0 | 0 |  |  | 0 | (!$self->get('filter') && $self->_get_interactive()) | 
| 2408 |  |  |  |  |  |  | and print $self->version(); | 
| 2409 |  |  |  |  |  |  |  | 
| 2410 |  |  |  |  |  |  | # Execute the initialization file. | 
| 2411 | 0 | 0 |  |  |  | 0 | eval { | 
| 2412 |  |  |  |  |  |  | $self->_execute_output( $self->init( | 
| 2413 |  |  |  |  |  |  | { level1 => delete $opt{level1} }, | 
| 2414 |  |  |  |  |  |  | delete $opt{initialization_file}, | 
| 2415 | 0 |  |  |  |  | 0 | ), $self->get( 'stdout' ) ); | 
| 2416 | 0 |  |  |  |  | 0 | 1; | 
| 2417 |  |  |  |  |  |  | } or warn $@;	# Not whinge, since presumably we already did. | 
| 2418 |  |  |  |  |  |  |  | 
| 2419 |  |  |  |  |  |  | # The remaining options set the corresponding attributes. | 
| 2420 | 0 | 0 |  |  |  | 0 | %opt and $self->set(%opt); | 
| 2421 |  |  |  |  |  |  |  | 
| 2422 |  |  |  |  |  |  | # Execution loop. What exit() really does is a last on this. | 
| 2423 |  |  |  |  |  |  | SATPASS2_EXECUTE: | 
| 2424 |  |  |  |  |  |  | { | 
| 2425 | 0 |  |  |  |  | 0 | $self->_execute( @args ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 2426 | 0 |  |  |  |  | 0 | while ( defined ( my $buffer = $in->( $self->get( 'prompt' ) ) ) ) { | 
| 2427 | 0 |  |  |  |  | 0 | $self->_execute( $in, $buffer ); | 
| 2428 |  |  |  |  |  |  | } | 
| 2429 |  |  |  |  |  |  | } | 
| 2430 | 0 |  |  |  |  | 0 | $self->_execute( q{echo ''} );	# The lazy way to be sure we | 
| 2431 |  |  |  |  |  |  | # have a newline before exit. | 
| 2432 | 0 |  |  |  |  | 0 | return; | 
| 2433 |  |  |  |  |  |  | } | 
| 2434 |  |  |  |  |  |  | } | 
| 2435 |  |  |  |  |  |  |  | 
| 2436 |  |  |  |  |  |  | sub save : Verb( changes! overwrite! ) { | 
| 2437 | 0 |  |  | 0 | 1 | 0 | my ( $self, $opt, $fn ) = __arguments( @_ ); | 
| 2438 |  |  |  |  |  |  |  | 
| 2439 | 0 | 0 |  |  |  | 0 | defined $fn or $fn = $self->initfile( { 'create-directory' => 1 } ); | 
| 2440 | 0 |  |  |  |  | 0 | chomp $fn;	# because initfile() adds a newline for printing | 
| 2441 | 0 | 0 | 0 |  |  | 0 | if ($fn ne '-' && -e $fn) { | 
| 2442 | 0 | 0 |  |  |  | 0 | -f $fn or $self->wail( | 
| 2443 |  |  |  |  |  |  | "Can not overwrite $fn: not an ordinary file"); | 
| 2444 | 0 | 0 |  |  |  | 0 | $opt->{overwrite} or do { | 
| 2445 | 0 |  |  |  |  | 0 | my $rslt = $self->_get_readline()->( | 
| 2446 |  |  |  |  |  |  | "File $fn exists. Overwrite [y/N]? "); | 
| 2447 | 0 | 0 |  |  |  | 0 | 'y' eq lc substr($rslt, 0, 1) | 
| 2448 |  |  |  |  |  |  | or return; | 
| 2449 |  |  |  |  |  |  | }; | 
| 2450 |  |  |  |  |  |  | } | 
| 2451 | 0 |  |  |  |  | 0 | my @show_opt; | 
| 2452 | 0 |  |  |  |  | 0 | my $title = 'settings'; | 
| 2453 | 0 | 0 |  |  |  | 0 | if ($opt->{changes}) { | 
| 2454 | 0 |  |  |  |  | 0 | push @show_opt, '-changes'; | 
| 2455 | 0 |  |  |  |  | 0 | $title = 'setting changes'; | 
| 2456 |  |  |  |  |  |  | } | 
| 2457 |  |  |  |  |  |  |  | 
| 2458 | 0 |  |  |  |  | 0 | my $output = <<"EOD" . | 
| 2459 |  |  |  |  |  |  |  | 
| 2460 |  |  |  |  |  |  | # Astro::App::Satpass2 $title | 
| 2461 |  |  |  |  |  |  |  | 
| 2462 |  |  |  |  |  |  | EOD | 
| 2463 |  |  |  |  |  |  | $self->show( @show_opt, qw{ -nodeprecated -noreadonly } ) . | 
| 2464 |  |  |  |  |  |  | <<"EOD" . $self->macro('list'); | 
| 2465 |  |  |  |  |  |  |  | 
| 2466 |  |  |  |  |  |  | # Astro::App::Satpass2 macros | 
| 2467 |  |  |  |  |  |  |  | 
| 2468 |  |  |  |  |  |  | EOD | 
| 2469 |  |  |  |  |  |  |  | 
| 2470 | 0 | 0 |  |  |  | 0 | if ( $self->{_perl} ) { | 
| 2471 | 0 |  |  |  |  | 0 | $output .= <<'EOD'; | 
| 2472 |  |  |  |  |  |  |  | 
| 2473 |  |  |  |  |  |  | # Astro::App::Satpass2 setup | 
| 2474 |  |  |  |  |  |  |  | 
| 2475 |  |  |  |  |  |  | EOD | 
| 2476 | 0 |  |  |  |  | 0 | foreach my $item ( @{ $self->{_perl} } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2477 | 0 |  |  |  |  | 0 | my ( $opt, @arg ) = @{ $item }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2478 | 0 |  |  |  |  | 0 | my @cmd = ( 'perl' ); | 
| 2479 | 0 |  |  |  |  | 0 | push @cmd, map { "-$_" } grep { $opt->{$_} } sort keys %{ $opt }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 2480 | 0 |  |  |  |  | 0 | $output .= join ' ', quoter( @cmd, @arg ); | 
| 2481 | 0 |  |  |  |  | 0 | $output .= "\n"; | 
| 2482 |  |  |  |  |  |  | } | 
| 2483 |  |  |  |  |  |  | } | 
| 2484 |  |  |  |  |  |  |  | 
| 2485 | 0 |  |  |  |  | 0 | foreach my $attribute ( qw{ formatter spacetrack time_parser } ) { | 
| 2486 | 0 | 0 |  |  |  | 0 | my $obj = $self->get( $attribute ) or next; | 
| 2487 | 0 | 0 | 0 |  |  | 0 | my $class = $obj->can( 'class_name_of_record' ) ? | 
| 2488 |  |  |  |  |  |  | $obj->class_name_of_record() : | 
| 2489 |  |  |  |  |  |  | ref $obj || $obj; | 
| 2490 | 0 |  | 0 |  |  | 0 | $output .= <<"EOD" . | 
| 2491 |  |  |  |  |  |  |  | 
| 2492 |  |  |  |  |  |  | # $class $title | 
| 2493 |  |  |  |  |  |  |  | 
| 2494 |  |  |  |  |  |  | EOD | 
| 2495 |  |  |  |  |  |  | ( $self->$attribute( $opt, 'config' ) || "# none\n" ); | 
| 2496 |  |  |  |  |  |  | } | 
| 2497 |  |  |  |  |  |  |  | 
| 2498 | 0 |  |  |  |  | 0 | $output .= $self->_save_sky( $opt ); | 
| 2499 |  |  |  |  |  |  |  | 
| 2500 | 0 | 0 |  |  |  | 0 | if ($fn ne '-') { | 
| 2501 | 0 | 0 |  |  |  | 0 | my $fh = IO::File->new( $fn, '>:encoding(utf-8)') | 
| 2502 |  |  |  |  |  |  | or $self->wail("Unable to open $fn: $!"); | 
| 2503 | 0 |  |  |  |  | 0 | print { $fh } $output; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2504 | 0 |  |  |  |  | 0 | $output = "$fn\n"; | 
| 2505 |  |  |  |  |  |  | } | 
| 2506 | 0 |  |  |  |  | 0 | return $output; | 
| 2507 | 20 |  |  | 20 |  | 22471 | } | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 153 |  | 
| 2508 |  |  |  |  |  |  |  | 
| 2509 |  |  |  |  |  |  | # Formats the commands to reconstitute the sky. This is only called from | 
| 2510 |  |  |  |  |  |  | # save(), but it is a subroutine for organizational reasons. | 
| 2511 |  |  |  |  |  |  | sub _save_sky { | 
| 2512 | 0 |  |  | 0 |  | 0 | my ( $self, $opt ) = @_; | 
| 2513 |  |  |  |  |  |  |  | 
| 2514 | 0 |  |  |  |  | 0 | my $output = <<'EOD'; | 
| 2515 |  |  |  |  |  |  |  | 
| 2516 |  |  |  |  |  |  | # Astro::App::Satpass2 sky | 
| 2517 |  |  |  |  |  |  |  | 
| 2518 |  |  |  |  |  |  | EOD | 
| 2519 |  |  |  |  |  |  |  | 
| 2520 | 0 |  |  |  |  | 0 | foreach my $body ( sort keys %{ $self->{sky_class} } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2521 |  |  |  |  |  |  | $opt->{changes} | 
| 2522 |  |  |  |  |  |  | and $sky_class{$body} | 
| 2523 | 0 | 0 | 0 |  |  | 0 | and $sky_class{$body} eq $self->{sky_class}{$body} | 
|  |  |  | 0 |  |  |  |  | 
| 2524 |  |  |  |  |  |  | and next; | 
| 2525 | 0 |  |  |  |  | 0 | $output .= $self->_sky_class_components( $body ) . "\n"; | 
| 2526 |  |  |  |  |  |  | } | 
| 2527 | 0 |  |  |  |  | 0 | foreach my $body ( sort keys ( %sky_class ) ) { | 
| 2528 | 0 | 0 |  |  |  | 0 | $self->{sky_class}{$body} | 
| 2529 |  |  |  |  |  |  | or $output .= $self->_sky_class_components( $body ) . "\n"; | 
| 2530 |  |  |  |  |  |  | } | 
| 2531 |  |  |  |  |  |  |  | 
| 2532 | 0 |  |  |  |  | 0 | my %exclude; | 
| 2533 | 0 | 0 |  |  |  | 0 | if ( $opt->{changes} ) { | 
| 2534 | 0 |  |  |  |  | 0 | %exclude = map { $_ => 1 } | 
|  | 0 |  |  |  |  | 0 |  | 
| 2535 |  |  |  |  |  |  | SUN_CLASS_DEFAULT, 'Astro::Coord::ECI::Moon'; | 
| 2536 | 0 |  |  |  |  | 0 | foreach my $name ( qw{ sun moon } ) { | 
| 2537 | 0 | 0 |  |  |  | 0 | defined $self->_find_in_sky( $name ) | 
| 2538 |  |  |  |  |  |  | or $output .= "sky drop $name\n"; | 
| 2539 |  |  |  |  |  |  | } | 
| 2540 |  |  |  |  |  |  | } else { | 
| 2541 | 0 |  |  |  |  | 0 | $output .= "sky clear\n"; | 
| 2542 |  |  |  |  |  |  | } | 
| 2543 | 0 |  |  |  |  | 0 | foreach my $body ( @{ $self->{sky} } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2544 | 0 | 0 |  |  |  | 0 | $exclude{ ref $body } | 
| 2545 |  |  |  |  |  |  | and next; | 
| 2546 | 0 |  |  |  |  | 0 | $output .= _sky_list_body( $body ); | 
| 2547 |  |  |  |  |  |  | } | 
| 2548 |  |  |  |  |  |  |  | 
| 2549 | 0 |  |  |  |  | 0 | return $output; | 
| 2550 |  |  |  |  |  |  | } | 
| 2551 |  |  |  |  |  |  |  | 
| 2552 |  |  |  |  |  |  | sub set : Verb() { | 
| 2553 | 72 |  |  | 72 | 1 | 496 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 2554 |  |  |  |  |  |  |  | 
| 2555 | 72 |  |  |  |  | 302 | while (@args) { | 
| 2556 | 351 |  |  |  |  | 936 | my ( $name, $value ) = splice @args, 0, 2; | 
| 2557 | 351 |  |  |  |  | 1002 | $self->_attribute_exists( $name ); | 
| 2558 | 351 | 100 |  |  |  | 795 | if ( _is_interactive() ) { | 
| 2559 | 28 | 100 |  |  |  | 148 | $nointeractive{$name} | 
| 2560 |  |  |  |  |  |  | and $self->wail( | 
| 2561 |  |  |  |  |  |  | "Attribute '$name' may not be set interactively"); | 
| 2562 | 27 | 50 | 66 |  |  | 207 | defined $value and $value eq 'undef' | 
| 2563 |  |  |  |  |  |  | and $value = undef; | 
| 2564 |  |  |  |  |  |  | } | 
| 2565 | 350 | 50 |  |  |  | 1207 | if ( $mutator{$name} ) { | 
| 2566 | 350 |  |  |  |  | 910 | $self->_deprecation_notice( attribute => $name ); | 
| 2567 | 350 |  |  |  |  | 1257 | $mutator{$name}->($self, $name, $value); | 
| 2568 |  |  |  |  |  |  | } else { | 
| 2569 | 0 |  |  |  |  | 0 | $self->wail("Read-only attribute '$name'"); | 
| 2570 |  |  |  |  |  |  | } | 
| 2571 |  |  |  |  |  |  | } | 
| 2572 | 71 |  |  |  |  | 200 | return; | 
| 2573 | 20 |  |  | 20 |  | 14033 | } | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 99 |  | 
| 2574 |  |  |  |  |  |  |  | 
| 2575 |  |  |  |  |  |  | sub _set_almanac_horizon { | 
| 2576 | 7 |  |  | 7 |  | 30 | my ( $self, $name, $value ) = @_; | 
| 2577 | 7 |  |  |  |  | 55 | my $parsed = $self->__parse_angle( { accept => 1 }, $value ); | 
| 2578 | 7 | 50 |  |  |  | 78 | my $internal = looks_like_number( $parsed ) ? deg2rad( $parsed ) : | 
| 2579 |  |  |  |  |  |  | $parsed; | 
| 2580 | 7 |  |  |  |  | 79 | my $eci = Astro::Coord::ECI->new(); | 
| 2581 | 7 |  |  |  |  | 660 | $eci->set( $name => $internal );	# To validate. | 
| 2582 | 7 |  |  |  |  | 272 | $self->{"_$name"} = $internal; | 
| 2583 | 7 |  |  |  |  | 99 | return( $self->{$name} = $parsed ); | 
| 2584 |  |  |  |  |  |  | } | 
| 2585 |  |  |  |  |  |  |  | 
| 2586 |  |  |  |  |  |  | { | 
| 2587 |  |  |  |  |  |  | my $plus_or_minus_90 = sub { $_[0] >= -90 && $_[0] <= 90 }; | 
| 2588 |  |  |  |  |  |  | my %validate = ( | 
| 2589 |  |  |  |  |  |  | horizon		=> $plus_or_minus_90, | 
| 2590 |  |  |  |  |  |  | latitude	=> $plus_or_minus_90, | 
| 2591 |  |  |  |  |  |  | longitude	=> sub { | 
| 2592 |  |  |  |  |  |  | $_[0] > 360 | 
| 2593 |  |  |  |  |  |  | and return 0; | 
| 2594 |  |  |  |  |  |  | $_[0] > 180 | 
| 2595 |  |  |  |  |  |  | and $_[0] -= 360; | 
| 2596 |  |  |  |  |  |  | $_[0] >= -180 && $_[0] <= 180; | 
| 2597 |  |  |  |  |  |  | }, | 
| 2598 |  |  |  |  |  |  | ); | 
| 2599 |  |  |  |  |  |  | sub _set_angle { | 
| 2600 | 31 |  |  | 31 |  | 121 | my ( $self, $name, $value ) = @_; | 
| 2601 | 31 |  |  |  |  | 110 | my $angle = $self->__parse_angle( $value ); | 
| 2602 | 31 | 100 |  |  |  | 137 | if ( my $code = $validate{$name} ) { | 
| 2603 | 15 | 0 |  |  |  | 74 | defined $angle or $self->weep( | 
|  |  | 50 |  |  |  |  |  | 
| 2604 |  |  |  |  |  |  | "$name angle is undef for value ", defined $value ? $value : 'undef' ); | 
| 2605 | 15 | 50 |  |  |  | 82 | $code->( $angle ) | 
| 2606 |  |  |  |  |  |  | or $self->wail( "Value $value is invalid for $name" ); | 
| 2607 |  |  |  |  |  |  | } | 
| 2608 | 31 |  |  |  |  | 154 | $self->{"_$name"} = deg2rad( $angle ); | 
| 2609 | 31 |  |  |  |  | 404 | return ( $self->{$name} = $angle ); | 
| 2610 |  |  |  |  |  |  | } | 
| 2611 |  |  |  |  |  |  | } | 
| 2612 |  |  |  |  |  |  |  | 
| 2613 |  |  |  |  |  |  | sub _set_angle_or_undef { | 
| 2614 | 21 |  |  | 21 |  | 84 | my ( $self, $name, $value ) = @_; | 
| 2615 | 21 | 100 | 66 |  |  | 138 | defined $value and 'undef' ne $value and goto &_set_angle; | 
| 2616 | 15 |  |  |  |  | 60 | return ( $self->{$name} = undef ); | 
| 2617 |  |  |  |  |  |  | } | 
| 2618 |  |  |  |  |  |  |  | 
| 2619 |  |  |  |  |  |  | sub _set_code_ref { | 
| 2620 | 11 | 50 |  | 11 |  | 55 | CODE_REF eq ref $_[2] | 
| 2621 |  |  |  |  |  |  | or $_[0]->wail( "Attribute $_[1] must be a code reference" ); | 
| 2622 | 11 |  |  |  |  | 67 | return( $_[0]{$_[1]} = $_[2] ); | 
| 2623 |  |  |  |  |  |  | } | 
| 2624 |  |  |  |  |  |  |  | 
| 2625 |  |  |  |  |  |  | # Set an attribute whose value is an Astro::App::Satpass2::Copier object | 
| 2626 |  |  |  |  |  |  | # %arg is a hash of argument name/value pairs: | 
| 2627 |  |  |  |  |  |  | #    {name} is the required name of the attribute to set; | 
| 2628 |  |  |  |  |  |  | #    {value} is the required value of the attribute to set; | 
| 2629 |  |  |  |  |  |  | #    {class} is the optional class that the object must be; | 
| 2630 |  |  |  |  |  |  | #    {default} is the optional default value if the required value is | 
| 2631 |  |  |  |  |  |  | #        undef or ''; | 
| 2632 |  |  |  |  |  |  | #    {undefined} is an optional value which, if true, permits the | 
| 2633 |  |  |  |  |  |  | #        attribute to be set to undef; | 
| 2634 |  |  |  |  |  |  | #    {nocopy} is an optional value which, if true, causes the old | 
| 2635 |  |  |  |  |  |  | #        object's attributes not to be copied to the new object; | 
| 2636 |  |  |  |  |  |  | #    {message} is an optional message to emit if the object can not be | 
| 2637 |  |  |  |  |  |  | #	instantiated; | 
| 2638 |  |  |  |  |  |  | #    {prefix} is an optional reference to an array of name prefixes to | 
| 2639 |  |  |  |  |  |  | #	try if the named module does not load. | 
| 2640 |  |  |  |  |  |  |  | 
| 2641 |  |  |  |  |  |  | sub _set_copyable { | 
| 2642 | 14 |  |  | 14 |  | 103 | my ( $self, %arg ) = @_; | 
| 2643 | 14 |  |  |  |  | 59 | my $old = $self->{$arg{name}}; | 
| 2644 | 14 |  |  |  |  | 30 | my $obj; | 
| 2645 | 14 | 50 |  |  |  | 58 | if ( ref $arg{value} ) { | 
| 2646 |  |  |  |  |  |  | blessed( $arg{value} ) | 
| 2647 | 0 | 0 |  |  |  | 0 | or $self->wail( "$arg{name} may not be unblessed reference" ); | 
| 2648 | 0 |  |  |  |  | 0 | $obj = $arg{value}; | 
| 2649 |  |  |  |  |  |  | $obj->can( 'warner' ) | 
| 2650 | 0 | 0 |  |  |  | 0 | and $obj->warner( $self->{_warner} ); | 
| 2651 |  |  |  |  |  |  | } else { | 
| 2652 | 14 | 50 |  |  |  | 64 | if ( defined $arg{default} ) { | 
| 2653 |  |  |  |  |  |  | defined $arg{value} | 
| 2654 |  |  |  |  |  |  | and '' ne $arg{value} | 
| 2655 | 14 | 50 | 33 |  |  | 92 | or $arg{value} = $arg{default}; | 
| 2656 |  |  |  |  |  |  | } | 
| 2657 | 14 | 50 | 33 |  |  | 118 | if ( ! defined $arg{value} || $arg{value} eq '' ) { | 
| 2658 |  |  |  |  |  |  | $arg{undefined} | 
| 2659 | 0 | 0 |  |  |  | 0 | or $self->wail( | 
| 2660 |  |  |  |  |  |  | "$arg{name} must be defined and not empty", | 
| 2661 |  |  |  |  |  |  | ); | 
| 2662 | 0 |  |  |  |  | 0 | return ( $self->{$arg{name}} = $arg{value} = undef ); | 
| 2663 |  |  |  |  |  |  | } | 
| 2664 | 14 |  |  |  |  | 82 | my ( $pkg, @args ) = $self->__parse_class_and_args( $arg{value} ); | 
| 2665 |  |  |  |  |  |  | my $cls = $self->load_package( | 
| 2666 | 14 | 50 |  |  |  | 64 | { fatal => 'wail' }, $pkg, @{ $arg{prefix} || [] } ); | 
|  | 14 |  |  |  |  | 92 |  | 
| 2667 | 14 | 50 | 33 |  |  | 266 | not $cls->can( 'init' ) | 
| 2668 |  |  |  |  |  |  | and _is_case_tolerant() | 
| 2669 |  |  |  |  |  |  | and $self->wail( | 
| 2670 |  |  |  |  |  |  | "$cls is missing methods. This can happen on a ", | 
| 2671 |  |  |  |  |  |  | 'case-tolerant system if you specify the class ', | 
| 2672 |  |  |  |  |  |  | 'name in the wrong case.' ); | 
| 2673 | 14 | 100 |  |  |  | 142 | $cls->can( 'parent' ) | 
| 2674 |  |  |  |  |  |  | and push @args, parent => $self; | 
| 2675 |  |  |  |  |  |  | $obj = $cls->new( | 
| 2676 |  |  |  |  |  |  | warner	=> $self->{_warner}, | 
| 2677 |  |  |  |  |  |  | @args, | 
| 2678 |  |  |  |  |  |  | ) | 
| 2679 |  |  |  |  |  |  | or $self->wail( $arg{message} || | 
| 2680 | 14 | 50 | 0 |  |  | 103 | "Can not instantiate object from '$arg{value}'" ); | 
| 2681 |  |  |  |  |  |  | } | 
| 2682 |  |  |  |  |  |  | defined $arg{class} | 
| 2683 |  |  |  |  |  |  | and not $obj->isa( $arg{class} ) | 
| 2684 | 14 | 50 | 66 |  |  | 156 | and $self->wail( "$arg{name} must be of class $arg{class}" ); | 
| 2685 |  |  |  |  |  |  | blessed( $old ) | 
| 2686 |  |  |  |  |  |  | and not $arg{nocopy} | 
| 2687 | 14 | 0 | 33 |  |  | 69 | and $old->can( 'copy' ) | 
|  |  |  | 33 |  |  |  |  | 
| 2688 |  |  |  |  |  |  | and $old->copy( $obj ); | 
| 2689 | 14 |  |  |  |  | 50 | $self->{$arg{name}} = $obj; | 
| 2690 | 14 |  |  |  |  | 100 | return $arg{value}; | 
| 2691 |  |  |  |  |  |  | } | 
| 2692 |  |  |  |  |  |  |  | 
| 2693 |  |  |  |  |  |  | sub _set_distance_meters { | 
| 2694 | 9 | 100 |  | 9 |  | 100 | return ( $_[0]{$_[1]} = defined $_[2] ? | 
| 2695 |  |  |  |  |  |  | ( $_[0]->__parse_distance( $_[2], '0m' ) * 1000 ) : $_[2] ); | 
| 2696 |  |  |  |  |  |  | } | 
| 2697 |  |  |  |  |  |  |  | 
| 2698 |  |  |  |  |  |  | sub _set_ellipsoid { | 
| 2699 | 7 |  |  | 7 |  | 25 | my ($self, $name, $val) = @_; | 
| 2700 | 7 |  |  |  |  | 74 | Astro::Coord::ECI->set (ellipsoid => $val); | 
| 2701 | 7 |  |  |  |  | 340 | return ($self->{$name} = $val); | 
| 2702 |  |  |  |  |  |  | } | 
| 2703 |  |  |  |  |  |  |  | 
| 2704 |  |  |  |  |  |  | sub _set_formatter { | 
| 2705 | 7 |  |  | 7 |  | 30 | my ( $self, $name, $val ) = @_; | 
| 2706 | 7 |  |  |  |  | 46 | return $self->_set_copyable( | 
| 2707 |  |  |  |  |  |  | name	=> $name, | 
| 2708 |  |  |  |  |  |  | value	=> $val, | 
| 2709 |  |  |  |  |  |  | message	=> 'Unknown formatter', | 
| 2710 |  |  |  |  |  |  | default	=> 'Astro::App::Satpass2::Format::Template', | 
| 2711 |  |  |  |  |  |  | prefix	=> [ 'Astro::App::Satpass2::Format' ] | 
| 2712 |  |  |  |  |  |  | ); | 
| 2713 |  |  |  |  |  |  | } | 
| 2714 |  |  |  |  |  |  |  | 
| 2715 |  |  |  |  |  |  | sub _set_formatter_attribute { | 
| 2716 | 24 |  |  | 24 |  | 117 | my ( $self, $name, $val ) = @_; | 
| 2717 | 24 |  |  |  |  | 78 | $self->get( 'formatter' )->$name( $val ); | 
| 2718 | 24 |  |  |  |  | 78 | return $val; | 
| 2719 |  |  |  |  |  |  | } | 
| 2720 |  |  |  |  |  |  |  | 
| 2721 |  |  |  |  |  |  | sub _set_geocoder { | 
| 2722 | 0 |  |  | 0 |  | 0 | my ( $self, $name, $val ) = @_; | 
| 2723 | 0 |  |  |  |  | 0 | return $self->_set_copyable( | 
| 2724 |  |  |  |  |  |  | name	=> $name, | 
| 2725 |  |  |  |  |  |  | value	=> $val, | 
| 2726 |  |  |  |  |  |  | class	=> 'Astro::App::Satpass2::Geocode', | 
| 2727 |  |  |  |  |  |  | message	=> 'Unknown formatter', | 
| 2728 |  |  |  |  |  |  | default	=> $default_geocoder->(), | 
| 2729 |  |  |  |  |  |  | undefined => 1, | 
| 2730 |  |  |  |  |  |  | nocopy	=> 1, | 
| 2731 |  |  |  |  |  |  | prefix	=> [ 'Astro::App::Satpass2::Geocode' ] | 
| 2732 |  |  |  |  |  |  | ); | 
| 2733 |  |  |  |  |  |  | } | 
| 2734 |  |  |  |  |  |  |  | 
| 2735 |  |  |  |  |  |  | sub _set_illum_class { | 
| 2736 | 7 |  |  | 7 |  | 27 | my ( $self, $name, $class ) = @_; | 
| 2737 | 7 |  |  |  |  | 21 | my $want_class = 'Astro::Coord::ECI'; | 
| 2738 | 7 | 50 |  |  |  | 34 | ref $class and $self->wail( "$name must not be a reference" ); | 
| 2739 | 7 | 50 |  |  |  | 34 | if ( defined $class ) { | 
| 2740 | 7 |  |  |  |  | 55 | $self->load_package( { fatal => 'wail' }, $class ); | 
| 2741 | 7 | 50 |  |  |  | 103 | $class->isa( $want_class ) | 
| 2742 |  |  |  |  |  |  | or $self->wail( "$name must be an $want_class" ); | 
| 2743 |  |  |  |  |  |  | } else { | 
| 2744 | 0 |  |  |  |  | 0 | $class = $want_class; | 
| 2745 |  |  |  |  |  |  | } | 
| 2746 | 7 |  |  |  |  | 33 | $self->{$name} = $class; | 
| 2747 | 7 |  |  |  |  | 24 | $self->{_help_module}{$name} = $class; | 
| 2748 | 7 |  |  |  |  | 14 | foreach my $body ( @{ $self->{bodies} } ) { | 
|  | 7 |  |  |  |  | 31 |  | 
| 2749 | 0 |  |  |  |  | 0 | $body->set( $name => $class ); | 
| 2750 |  |  |  |  |  |  | } | 
| 2751 | 7 |  |  |  |  | 25 | return; | 
| 2752 |  |  |  |  |  |  | } | 
| 2753 |  |  |  |  |  |  |  | 
| 2754 |  |  |  |  |  |  | sub _set_model { | 
| 2755 | 7 |  |  | 7 |  | 37 | my ( $self, $name, $val ) = @_; | 
| 2756 | 7 | 50 |  |  |  | 74 | Astro::Coord::ECI::TLE->is_valid_model( $val ) | 
| 2757 |  |  |  |  |  |  | or $self->wail( | 
| 2758 |  |  |  |  |  |  | "'$val' is not a valid Astro::Coord::ECI::TLE model" ); | 
| 2759 | 7 |  |  |  |  | 59 | foreach my $body ( @{ $self->{bodies} } ) { | 
|  | 7 |  |  |  |  | 32 |  | 
| 2760 | 0 |  |  |  |  | 0 | $body->set( model => $val ); | 
| 2761 |  |  |  |  |  |  | } | 
| 2762 | 7 |  |  |  |  | 43 | return ( $self->{$name} = $val ); | 
| 2763 |  |  |  |  |  |  | } | 
| 2764 |  |  |  |  |  |  |  | 
| 2765 |  |  |  |  |  |  | sub _set_output_layers { | 
| 2766 | 7 |  |  | 7 |  | 32 | my ( $self, $name, $val ) = @_; | 
| 2767 |  |  |  |  |  |  |  | 
| 2768 | 7 | 50 | 33 |  |  | 64 | if ( defined $val && '' ne $val ) { | 
| 2769 | 7 | 50 |  | 7 |  | 54 | open my $fh, ">$val", File::Spec->devnull() | 
|  | 7 |  |  |  |  | 15 |  | 
|  | 7 |  |  |  |  | 103 |  | 
|  | 7 |  |  |  |  | 421 |  | 
| 2770 |  |  |  |  |  |  | or $self->wail( "Invalid $name value '$val'" ); | 
| 2771 | 7 |  |  |  |  | 85532 | close $fh; | 
| 2772 |  |  |  |  |  |  | } | 
| 2773 | 7 |  |  |  |  | 91 | return ( $self->{$name} = $val ); | 
| 2774 |  |  |  |  |  |  | } | 
| 2775 |  |  |  |  |  |  |  | 
| 2776 |  |  |  |  |  |  | { | 
| 2777 |  |  |  |  |  |  | my %variant_def = ( | 
| 2778 |  |  |  |  |  |  | visible_events	=> PASS_VARIANT_VISIBLE_EVENTS, | 
| 2779 |  |  |  |  |  |  | fake_max	=> PASS_VARIANT_FAKE_MAX, | 
| 2780 |  |  |  |  |  |  | start_end	=> PASS_VARIANT_START_END, | 
| 2781 |  |  |  |  |  |  | no_illumination	=> PASS_VARIANT_NO_ILLUMINATION, | 
| 2782 |  |  |  |  |  |  | brightest	=> PASS_VARIANT_BRIGHTEST, | 
| 2783 |  |  |  |  |  |  | ); | 
| 2784 |  |  |  |  |  |  |  | 
| 2785 |  |  |  |  |  |  | my @option_names; | 
| 2786 |  |  |  |  |  |  | foreach my $key ( keys %variant_def ) { | 
| 2787 |  |  |  |  |  |  | if ( $key =~ m/ _ /smx ) { | 
| 2788 |  |  |  |  |  |  | ( my $dashed = $key ) =~ s/ _ /-/smxg; | 
| 2789 |  |  |  |  |  |  | $key = "$key|$dashed"; | 
| 2790 |  |  |  |  |  |  | } | 
| 2791 |  |  |  |  |  |  | push @option_names, "$key!"; | 
| 2792 |  |  |  |  |  |  | } | 
| 2793 |  |  |  |  |  |  |  | 
| 2794 |  |  |  |  |  |  | my $go; | 
| 2795 |  |  |  |  |  |  |  | 
| 2796 |  |  |  |  |  |  | sub _set_pass_variant { | 
| 2797 | 8 |  |  | 8 |  | 31 | my ( $self, $name, $val ) = @_; | 
| 2798 | 8 | 100 |  |  |  | 82 | if ( $val =~ m/ \A (?: 0 x? ) [0-9]* \z /smx ) { | 
|  |  | 50 |  |  |  |  |  | 
| 2799 | 7 |  |  |  |  | 33 | $val = oct $val; | 
| 2800 |  |  |  |  |  |  | } elsif ( $val !~ m/ \A [0-9]+ \z /smx ) { | 
| 2801 | 1 |  |  |  |  | 18 | my @args = split qr{ [^\w-] }smx, $val; | 
| 2802 | 1 |  |  |  |  | 6 | foreach ( @args ) { | 
| 2803 | 1 |  |  |  |  | 7 | s/ \A (?! - ) /-/smx; | 
| 2804 |  |  |  |  |  |  | } | 
| 2805 | 1 |  | 33 |  |  | 16 | $go ||= Getopt::Long::Parser->new(); | 
| 2806 | 1 |  |  |  |  | 25 | $val = $self->get( $name ); | 
| 2807 |  |  |  |  |  |  | $go->getoptionsfromarray( \@args, | 
| 2808 | 0 |  |  | 0 |  | 0 | none	=> sub { $val = PASS_VARIANT_NONE }, | 
| 2809 | 1 | 50 |  |  |  | 14 | map { $_ => sub { | 
| 2810 | 1 |  |  | 1 |  | 668 | my ( $name, $value ) = @_; | 
| 2811 | 1 |  |  |  |  | 6 | my $mask = $variant_def{$name}; | 
| 2812 | 1 | 50 |  |  |  | 11 | if ( $value ) { | 
| 2813 | 0 |  |  |  |  | 0 | $val |= $mask; | 
| 2814 |  |  |  |  |  |  | } else { | 
| 2815 | 1 |  |  |  |  | 4 | $val &= ~ $mask; | 
| 2816 |  |  |  |  |  |  | } | 
| 2817 | 1 |  |  |  |  | 3 | return; | 
| 2818 |  |  |  |  |  |  | } | 
| 2819 | 5 |  |  |  |  | 37 | } @option_names ) | 
| 2820 |  |  |  |  |  |  | or $self->wail( "Invalid $name value '$val'" ); | 
| 2821 |  |  |  |  |  |  | } | 
| 2822 | 8 |  |  |  |  | 151 | return ( $self->{$name} = $val ); | 
| 2823 |  |  |  |  |  |  | } | 
| 2824 |  |  |  |  |  |  |  | 
| 2825 |  |  |  |  |  |  | sub _show_pass_variant { | 
| 2826 | 1 |  |  | 1 |  | 3 | my ( $self, $name ) = @_; | 
| 2827 | 1 |  |  |  |  | 4 | my $val = $self->get( $name ); | 
| 2828 | 1 |  |  |  |  | 3 | my @options; | 
| 2829 | 1 |  |  |  |  | 4 | foreach my $key ( keys %variant_def ) { | 
| 2830 | 5 | 50 |  |  |  | 12 | $val & $variant_def{$key} | 
| 2831 |  |  |  |  |  |  | and push @options, "$key"; | 
| 2832 |  |  |  |  |  |  | } | 
| 2833 |  |  |  |  |  |  | @options | 
| 2834 | 1 | 50 |  |  |  | 5 | or push @options, 'none'; | 
| 2835 | 1 |  |  |  |  | 6 | return ( set => $name, join ',', @options ); | 
| 2836 |  |  |  |  |  |  | } | 
| 2837 |  |  |  |  |  |  |  | 
| 2838 |  |  |  |  |  |  | sub want_pass_variant { | 
| 2839 | 138 |  |  | 138 | 1 | 2887 | my ( $self, $variant ) = @_; | 
| 2840 | 138 | 50 |  |  |  | 493 | $variant_def{$variant} | 
| 2841 |  |  |  |  |  |  | or $self->wail( "Invalid pass_variant name '$variant'" ); | 
| 2842 | 138 |  |  |  |  | 465 | my $val = $self->get( 'pass_variant' ) & $variant_def{$variant}; | 
| 2843 | 138 |  |  |  |  | 460 | return $val; | 
| 2844 |  |  |  |  |  |  | } | 
| 2845 |  |  |  |  |  |  |  | 
| 2846 |  |  |  |  |  |  | } | 
| 2847 |  |  |  |  |  |  |  | 
| 2848 |  |  |  |  |  |  | sub _set_spacetrack { | 
| 2849 | 0 |  |  | 0 |  | 0 | my ($self, $name, $val) = @_; | 
| 2850 | 0 | 0 |  |  |  | 0 | if (defined $val) { | 
| 2851 | 0 | 0 |  |  |  | 0 | instance($val, 'Astro::SpaceTrack') | 
| 2852 |  |  |  |  |  |  | or $self->wail("$name must be an Astro::SpaceTrack instance"); | 
| 2853 | 0 |  |  |  |  | 0 | my $version = $val->VERSION(); | 
| 2854 | 0 |  |  |  |  | 0 | $version =~ s/ _ //smxg; | 
| 2855 | 0 | 0 |  |  |  | 0 | $version >= ASTRO_SPACETRACK_VERSION | 
| 2856 |  |  |  |  |  |  | or $self->wail("$name must be Astro::SpaceTrack version ", | 
| 2857 |  |  |  |  |  |  | ASTRO_SPACETRACK_VERSION, ' or greater' ); | 
| 2858 |  |  |  |  |  |  | } | 
| 2859 | 0 |  |  |  |  | 0 | return ($self->{$name} = $val); | 
| 2860 |  |  |  |  |  |  | } | 
| 2861 |  |  |  |  |  |  |  | 
| 2862 |  |  |  |  |  |  | sub _set_stdout { | 
| 2863 | 15 |  |  | 15 |  | 43 | my ($self, $name, $val) = @_; | 
| 2864 |  |  |  |  |  |  | $self->{frame} | 
| 2865 | 15 | 50 |  |  |  | 72 | and $self->{frame}[-1]{$name} = $val; | 
| 2866 | 15 |  |  |  |  | 69 | return ($self->{$name} = $val); | 
| 2867 |  |  |  |  |  |  | } | 
| 2868 |  |  |  |  |  |  |  | 
| 2869 |  |  |  |  |  |  | sub _set_sun_class { | 
| 2870 | 0 |  |  | 0 |  | 0 | my ( $self, $name, $val ) = @_; | 
| 2871 | 0 |  |  |  |  | 0 | $self->_attribute_exists( $name ); | 
| 2872 | 0 |  |  |  |  | 0 | return $self->sky( class => $name, $val ); | 
| 2873 |  |  |  |  |  |  | } | 
| 2874 |  |  |  |  |  |  |  | 
| 2875 |  |  |  |  |  |  | sub _set_time_parser { | 
| 2876 | 7 |  |  | 7 |  | 32 | my ( $self, $name, $val ) = @_; | 
| 2877 |  |  |  |  |  |  |  | 
| 2878 | 7 | 50 | 33 |  |  | 83 | if ( CODE_REF eq ref $val ) { | 
|  |  | 50 |  |  |  |  |  | 
| 2879 | 0 |  |  |  |  | 0 | $val = _set_time_parser_code( $val ); | 
| 2880 |  |  |  |  |  |  | } elsif ( defined $val and my $macro = $self->{macro}{$val} ) { | 
| 2881 | 0 |  |  |  |  | 0 | $val = _set_time_parser_code( | 
| 2882 |  |  |  |  |  |  | $macro->implements( $val, required => 1 ), | 
| 2883 |  |  |  |  |  |  | $val, | 
| 2884 |  |  |  |  |  |  | ); | 
| 2885 |  |  |  |  |  |  | } | 
| 2886 |  |  |  |  |  |  |  | 
| 2887 | 7 |  |  |  |  | 53 | return $self->_set_copyable( | 
| 2888 |  |  |  |  |  |  | name	=> $name, | 
| 2889 |  |  |  |  |  |  | value	=> $val, | 
| 2890 |  |  |  |  |  |  | class	=> 'Astro::App::Satpass2::ParseTime', | 
| 2891 |  |  |  |  |  |  | message	=> 'Unknown time parser', | 
| 2892 |  |  |  |  |  |  | default	=> 'Astro::App::Satpass2::ParseTime', | 
| 2893 |  |  |  |  |  |  | nocopy	=> 1, | 
| 2894 |  |  |  |  |  |  | prefix	=> [ 'Astro::App::Satpass2::ParseTime' ], | 
| 2895 |  |  |  |  |  |  | ); | 
| 2896 |  |  |  |  |  |  | } | 
| 2897 |  |  |  |  |  |  |  | 
| 2898 |  |  |  |  |  |  | sub _set_time_parser_attribute { | 
| 2899 | 14 |  |  | 14 |  | 41 | my ( $self, $name, $val ) = @_; | 
| 2900 | 14 | 50 | 66 |  |  | 91 | defined $val and $val eq 'undef' and $val = undef; | 
| 2901 | 14 |  |  |  |  | 160 | $self->{time_parser}->$name( $val ); | 
| 2902 | 14 |  |  |  |  | 36 | return $val; | 
| 2903 |  |  |  |  |  |  | } | 
| 2904 |  |  |  |  |  |  |  | 
| 2905 |  |  |  |  |  |  | sub _set_time_parser_code { | 
| 2906 | 0 |  |  | 0 |  | 0 | my ( $code, $name ) = @_; | 
| 2907 | 0 |  |  |  |  | 0 | require Astro::App::Satpass2::ParseTime::Code; | 
| 2908 | 0 |  |  |  |  | 0 | my $obj = Astro::App::Satpass2::ParseTime::Code->new(); | 
| 2909 | 0 |  |  |  |  | 0 | return $obj->code( $code, $name ); | 
| 2910 |  |  |  |  |  |  | } | 
| 2911 |  |  |  |  |  |  |  | 
| 2912 |  |  |  |  |  |  | _frame_pop_force_set ( 'twilight' );	# Force use of the set() method | 
| 2913 |  |  |  |  |  |  | # in _frame_pop(), because we | 
| 2914 |  |  |  |  |  |  | # need to set {_twilight} as | 
| 2915 |  |  |  |  |  |  | # well. | 
| 2916 |  |  |  |  |  |  | sub _set_twilight { | 
| 2917 | 9 |  |  | 9 |  | 36 | my ($self, $name, $val) = @_; | 
| 2918 | 9 | 50 |  |  |  | 65 | if (my $key = $twilight_abbr{lc $val}) { | 
| 2919 | 9 |  |  |  |  | 32 | $self->{$name} = $key; | 
| 2920 | 9 |  |  |  |  | 36 | $self->{_twilight} = $twilight_def{$key}; | 
| 2921 |  |  |  |  |  |  | } else { | 
| 2922 | 0 |  |  |  |  | 0 | my $angle = $self->__parse_angle( { accept => 1 }, $val ); | 
| 2923 | 0 | 0 |  |  |  | 0 | looks_like_number( $angle ) | 
| 2924 |  |  |  |  |  |  | or $self->wail( 'Twilight must be number or known keyword' ); | 
| 2925 | 0 |  |  |  |  | 0 | $self->{$name} = $val; | 
| 2926 | 0 |  |  |  |  | 0 | $self->{_twilight} = deg2rad ($angle); | 
| 2927 |  |  |  |  |  |  | } | 
| 2928 | 9 |  |  |  |  | 31 | return $val; | 
| 2929 |  |  |  |  |  |  | } | 
| 2930 |  |  |  |  |  |  |  | 
| 2931 |  |  |  |  |  |  | sub _set_tz { | 
| 2932 | 7 |  |  | 7 |  | 35 | my ( $self, $name, $val ) = @_; | 
| 2933 | 7 |  |  |  |  | 33 | $self->_set_formatter_attribute( $name, $val ); | 
| 2934 | 7 |  |  |  |  | 49 | $self->_set_time_parser_attribute( $name, $val ); | 
| 2935 | 7 |  |  |  |  | 24 | return $val; | 
| 2936 |  |  |  |  |  |  | } | 
| 2937 |  |  |  |  |  |  |  | 
| 2938 |  |  |  |  |  |  | sub _set_unmodified { | 
| 2939 | 165 |  |  | 165 |  | 687 | return ($_[0]{$_[1]} = $_[2]); | 
| 2940 |  |  |  |  |  |  | } | 
| 2941 |  |  |  |  |  |  |  | 
| 2942 |  |  |  |  |  |  | sub _set_warner_attribute { | 
| 2943 | 0 |  |  | 0 |  | 0 | my ( $self, $name, $val ) = @_; | 
| 2944 | 0 | 0 | 0 |  |  | 0 | defined $val and $val eq 'undef' and $val = undef; | 
| 2945 | 0 |  |  |  |  | 0 | $self->{_warner}->$name( $val ); | 
| 2946 | 0 |  |  |  |  | 0 | return $val; | 
| 2947 |  |  |  |  |  |  | } | 
| 2948 |  |  |  |  |  |  |  | 
| 2949 |  |  |  |  |  |  | sub _set_webcmd { | 
| 2950 | 7 |  |  | 7 |  | 27 | my ($self, $name, $val) = @_; | 
| 2951 |  |  |  |  |  |  | # TODO warn if $val is true but not '1'. | 
| 2952 | 7 | 50 |  |  |  | 29 | if ( my $st = $self->get( 'spacetrack' ) ) { | 
| 2953 |  |  |  |  |  |  | # TODO once spacetrack supports '1', just pass $val. | 
| 2954 | 0 |  |  |  |  | 0 | $st->set( webcmd => $self->_get_browser_command( $val ) ); | 
| 2955 |  |  |  |  |  |  | } | 
| 2956 | 7 |  |  |  |  | 36 | return ($self->{$name} = $val); | 
| 2957 |  |  |  |  |  |  | } | 
| 2958 |  |  |  |  |  |  |  | 
| 2959 |  |  |  |  |  |  | sub show : Verb( changes! deprecated! readonly! ) { | 
| 2960 | 23 |  |  | 23 | 1 | 114 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 2961 |  |  |  |  |  |  |  | 
| 2962 | 23 |  |  |  |  | 109 | foreach my $name ( qw{ deprecated readonly } ) { | 
| 2963 | 46 | 50 |  |  |  | 196 | exists $opt->{$name} or $opt->{$name} = 1; | 
| 2964 |  |  |  |  |  |  | } | 
| 2965 | 23 |  |  |  |  | 48 | my $output; | 
| 2966 |  |  |  |  |  |  |  | 
| 2967 | 23 | 50 |  |  |  | 70 | unless ( @args ) { | 
| 2968 | 0 |  |  |  |  | 0 | foreach my $name ( sort keys %accessor ) { | 
| 2969 | 0 | 0 |  |  |  | 0 | $self->_attribute_exists( $name, query => 1 ) | 
| 2970 |  |  |  |  |  |  | or next; | 
| 2971 | 0 | 0 |  |  |  | 0 | $nointeractive{$name} | 
| 2972 |  |  |  |  |  |  | and next; | 
| 2973 |  |  |  |  |  |  | exists $mutator{$name} | 
| 2974 |  |  |  |  |  |  | or $opt->{readonly} | 
| 2975 | 0 | 0 | 0 |  |  | 0 | or next; | 
| 2976 | 0 |  |  |  |  | 0 | my $depr; | 
| 2977 |  |  |  |  |  |  | ( $depr = $self->_deprecation_in_progress( attribute => | 
| 2978 |  |  |  |  |  |  | $name ) ) | 
| 2979 | 0 | 0 | 0 |  |  | 0 | and ( not $opt->{deprecated} or $depr >= 3 ) | 
|  |  |  | 0 |  |  |  |  | 
| 2980 |  |  |  |  |  |  | and next; | 
| 2981 | 0 |  |  |  |  | 0 | push @args, $name; | 
| 2982 |  |  |  |  |  |  | } | 
| 2983 |  |  |  |  |  |  | } | 
| 2984 |  |  |  |  |  |  |  | 
| 2985 | 23 |  |  |  |  | 59 | foreach my $name (@args) { | 
| 2986 | 23 | 50 |  |  |  | 89 | exists $shower{$name} | 
| 2987 |  |  |  |  |  |  | or $self->wail("No such attribute as '$name'"); | 
| 2988 |  |  |  |  |  |  |  | 
| 2989 | 23 |  |  |  |  | 134 | my @val = $shower{$name}->( $self, $name ); | 
| 2990 | 23 | 50 |  |  |  | 92 | if ( $opt->{changes} ) { | 
| 2991 | 20 |  |  | 20 |  | 73943 | no warnings qw{ uninitialized }; | 
|  | 20 |  |  |  |  | 71 |  | 
|  | 20 |  |  |  |  | 3027 |  | 
| 2992 | 0 | 0 |  |  |  | 0 | $static{$name} eq $val[-1] and next; | 
| 2993 |  |  |  |  |  |  | } | 
| 2994 |  |  |  |  |  |  |  | 
| 2995 | 23 | 50 |  |  |  | 96 | exists $mutator{$name} or unshift @val, '#'; | 
| 2996 | 23 |  |  |  |  | 111 | $output .= quoter( @val ) . "\n"; | 
| 2997 |  |  |  |  |  |  | } | 
| 2998 | 23 |  |  |  |  | 121 | return $output; | 
| 2999 | 20 |  |  | 20 |  | 177 | } | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 107 |  | 
| 3000 |  |  |  |  |  |  |  | 
| 3001 |  |  |  |  |  |  | sub _show_copyable { | 
| 3002 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 3003 | 0 |  |  |  |  | 0 | my $obj = $self->get( $name ); | 
| 3004 | 0 |  |  |  |  | 0 | my $val = $obj->class_name_of_record(); | 
| 3005 | 0 |  |  |  |  | 0 | return ( 'set', $name, $val ); | 
| 3006 |  |  |  |  |  |  | } | 
| 3007 |  |  |  |  |  |  |  | 
| 3008 |  |  |  |  |  |  | sub _show_formatter_attribute { | 
| 3009 | 2 |  |  | 2 |  | 7 | my ( $self, $name ) = @_; | 
| 3010 | 2 |  |  |  |  | 10 | my $val = $self->{formatter}->decode( $name ); | 
| 3011 | 2 |  |  |  |  | 7 | return ( qw{ formatter }, $name, $val ); | 
| 3012 |  |  |  |  |  |  | } | 
| 3013 |  |  |  |  |  |  |  | 
| 3014 |  |  |  |  |  |  | sub _show_sun_class { | 
| 3015 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 3016 | 0 |  |  |  |  | 0 | $self->_attribute_exists( $name ); | 
| 3017 | 0 |  |  |  |  | 0 | return $self->_sky_class_components( $name ); | 
| 3018 |  |  |  |  |  |  | } | 
| 3019 |  |  |  |  |  |  |  | 
| 3020 |  |  |  |  |  |  | sub _show_time_parser { | 
| 3021 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 3022 | 0 |  |  |  |  | 0 | my $obj = $self->get( $name ); | 
| 3023 | 0 |  |  |  |  | 0 | my $val = $obj->class_name_of_record(); | 
| 3024 | 0 | 0 |  |  |  | 0 | if ( my $back_end = $obj->back_end() ) { | 
| 3025 | 0 |  |  |  |  | 0 | $val = "$val,back_end=$back_end"; | 
| 3026 |  |  |  |  |  |  | } | 
| 3027 | 0 |  |  |  |  | 0 | return ( set => $name, $val ); | 
| 3028 |  |  |  |  |  |  | } | 
| 3029 |  |  |  |  |  |  |  | 
| 3030 |  |  |  |  |  |  | sub _show_unmodified { | 
| 3031 | 20 |  |  | 20 |  | 61 | my ($self, $name) = @_; | 
| 3032 | 20 |  |  |  |  | 70 | my $val = $self->get( $name ); | 
| 3033 | 20 |  |  |  |  | 105 | return ( 'set', $name, $val ); | 
| 3034 |  |  |  |  |  |  | } | 
| 3035 |  |  |  |  |  |  |  | 
| 3036 |  |  |  |  |  |  | # For proper motion, we need to convert arc seconds per year to degrees | 
| 3037 |  |  |  |  |  |  | # per second. Perl::Critic does not like 'use constant' because they do | 
| 3038 |  |  |  |  |  |  | # not interpolate, but they really do: "@{[SPY2DPS]}". | 
| 3039 |  |  |  |  |  |  |  | 
| 3040 | 20 |  |  | 20 |  | 10865 | use constant SPY2DPS => 3600 * 365.24219 * SECSPERDAY; | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 8815 |  | 
| 3041 |  |  |  |  |  |  |  | 
| 3042 |  |  |  |  |  |  | # Given a body in the sky, encodes it in 'sky add' format | 
| 3043 |  |  |  |  |  |  | sub _sky_list_body { | 
| 3044 | 8 |  |  | 8 |  | 23 | my ( $body ) = @_; | 
| 3045 | 8 | 50 |  |  |  | 25 | if ( embodies( $body, 'Astro::Coord::ECI::TLE' ) ) { | 
|  |  | 100 |  |  |  |  |  | 
| 3046 | 0 |  |  |  |  | 0 | return sprintf "sky tle %s\n", quoter( | 
| 3047 |  |  |  |  |  |  | $body->get( 'tle' ) ); | 
| 3048 |  |  |  |  |  |  | } elsif ( $body->isa( 'Astro::Coord::ECI::Star' ) ) { | 
| 3049 | 1 |  |  |  |  | 57 | my ( $ra, $dec, $rng, $pmra, $pmdec, $vr ) = $body->position(); | 
| 3050 | 1 |  |  |  |  | 17 | $rng /= PARSEC; | 
| 3051 | 1 |  |  |  |  | 9 | $pmra = rad2deg( $pmra / 24 * 360 * cos( $ra ) ) * SPY2DPS; | 
| 3052 | 1 |  |  |  |  | 7 | $pmdec = rad2deg( $pmdec ) * SPY2DPS; | 
| 3053 | 1 |  |  |  |  | 5 | return sprintf | 
| 3054 |  |  |  |  |  |  | "sky add %s %s %7.3f %.2f %.4f %.5f %s\n", | 
| 3055 |  |  |  |  |  |  | quoter( $body->get( 'name' ) ), _rad2hms( $ra ), | 
| 3056 |  |  |  |  |  |  | rad2deg( $dec ), $rng, $pmra, $pmdec, $vr; | 
| 3057 |  |  |  |  |  |  | } else { | 
| 3058 | 7 |  |  |  |  | 239 | return sprintf "sky add %s\n", quoter( $body->get( 'name' ) ); | 
| 3059 |  |  |  |  |  |  | } | 
| 3060 |  |  |  |  |  |  | } | 
| 3061 |  |  |  |  |  |  |  | 
| 3062 |  |  |  |  |  |  | sub sky : Verb() Tweak( -completion _readline_complete_subcommand ) { | 
| 3063 | 12 |  |  | 12 | 1 | 61 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 3064 |  |  |  |  |  |  |  | 
| 3065 | 12 |  | 50 |  |  | 84 | my $verb = lc ( shift @args || 'list' ); | 
| 3066 |  |  |  |  |  |  |  | 
| 3067 | 12 | 50 |  |  |  | 98 | if ( my $code = $self->can( "_sky_sub_$verb") ) { | 
| 3068 | 12 |  |  |  |  | 47 | return $code->( $self, @args ); | 
| 3069 |  |  |  |  |  |  | } else { | 
| 3070 | 0 |  |  |  |  | 0 | $self->wail("'sky' subcommand '$verb' not known"); | 
| 3071 |  |  |  |  |  |  | } | 
| 3072 | 0 |  |  |  |  | 0 | return;	# We can't get here, but Perl::Critic does not know this. | 
| 3073 | 20 |  |  | 20 |  | 178 | } | 
|  | 20 |  |  |  |  | 74 |  | 
|  | 20 |  |  |  |  | 136 |  | 
| 3074 |  |  |  |  |  |  |  | 
| 3075 |  |  |  |  |  |  | # Given the name of a potential background object, return its | 
| 3076 |  |  |  |  |  |  | # definition. This is an array in list context, or a quoted string in | 
| 3077 |  |  |  |  |  |  | # scalar context. | 
| 3078 |  |  |  |  |  |  | sub _sky_class_components { | 
| 3079 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 3080 | 0 | 0 |  |  |  | 0 | my $info = $self->{sky_class}{ fold_case( $name ) } | 
| 3081 |  |  |  |  |  |  | or $self->weep( "No class defined for $name" ); | 
| 3082 | 0 |  |  |  |  | 0 | my ( $class, @attr ) = @{ $info }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3083 |  |  |  |  |  |  | # We rely on sky( class => $name, $class, ... ) keeping the name | 
| 3084 |  |  |  |  |  |  | # last. | 
| 3085 | 0 |  |  |  |  | 0 | $name = pop @attr; | 
| 3086 | 0 |  |  |  |  | 0 | pop @attr;	# 'name'; | 
| 3087 | 0 |  |  |  |  | 0 | my @parts = ( qw{ sky class }, $name, $class, @attr ); | 
| 3088 |  |  |  |  |  |  | wantarray | 
| 3089 | 0 | 0 |  |  |  | 0 | and return @parts; | 
| 3090 | 0 |  |  |  |  | 0 | return join ' ', map { quoter( $_ ) } @parts; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3091 |  |  |  |  |  |  | } | 
| 3092 |  |  |  |  |  |  |  | 
| 3093 |  |  |  |  |  |  | # Given the name of a potential sky object, instantiate it. Named | 
| 3094 |  |  |  |  |  |  | # arguments are optional; the following are supported: | 
| 3095 |  |  |  |  |  |  | #   fatal = Whether failure to find the name is fatal. Default is true. | 
| 3096 |  |  |  |  |  |  | sub _sky_object { | 
| 3097 | 12 |  |  | 12 |  | 82 | my ( $self, $name, %opt ) = @_; | 
| 3098 |  |  |  |  |  |  | defined $opt{fatal} | 
| 3099 | 12 | 100 |  |  |  | 71 | or $opt{fatal} = 1; | 
| 3100 | 12 | 100 |  |  |  | 94 | if ( my $info = $self->{sky_class}{ fold_case( $name ) } ) { | 
|  |  | 50 |  |  |  |  |  | 
| 3101 | 10 |  |  |  |  | 30 | my ( $class, @attr ) = @{ $info }; | 
|  | 10 |  |  |  |  | 87 |  | 
| 3102 | 10 |  |  |  |  | 120 | return $class->new( @attr ); | 
| 3103 |  |  |  |  |  |  | } elsif ( $opt{fatal} ) { | 
| 3104 | 0 |  |  |  |  | 0 | $self->weep( "No class defined for $name" ); | 
| 3105 |  |  |  |  |  |  | } | 
| 3106 | 2 |  |  |  |  | 10 | return; | 
| 3107 |  |  |  |  |  |  | } | 
| 3108 |  |  |  |  |  |  |  | 
| 3109 |  |  |  |  |  |  | # Calls to the following _sky_sub_... methods are generated dynamically | 
| 3110 |  |  |  |  |  |  | # above, so there is no way Perl::Critic can find them. | 
| 3111 |  |  |  |  |  |  | # | 
| 3112 |  |  |  |  |  |  | sub _sky_sub_add : Verb()  {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3113 | 5 |  |  | 5 |  | 17 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 3114 | 5 | 50 |  |  |  | 38 | my $name = shift @args | 
| 3115 |  |  |  |  |  |  | or $self->wail( 'You did not specify what to add' ); | 
| 3116 | 5 | 50 |  |  |  | 26 | defined $self->_find_in_sky( $name ) | 
| 3117 |  |  |  |  |  |  | and return; | 
| 3118 | 5 | 100 |  |  |  | 21 | if ( my $obj = $self->_sky_object( $name, fatal => 0 ) ) { | 
| 3119 | 3 |  |  |  |  | 307 | push @{ $self->{sky} }, $obj; | 
|  | 3 |  |  |  |  | 11 |  | 
| 3120 |  |  |  |  |  |  | } else { | 
| 3121 | 2 | 100 |  |  |  | 19 | @args >= 2 | 
| 3122 |  |  |  |  |  |  | or $self->wail( | 
| 3123 |  |  |  |  |  |  | 'You must give at least right ascension and declination' ); | 
| 3124 | 1 |  |  |  |  | 14 | my $ra = deg2rad( $self->__parse_angle( shift @args ) ); | 
| 3125 | 1 |  |  |  |  | 9 | my $dec = deg2rad( $self->__parse_angle( shift @args ) ); | 
| 3126 | 1 | 50 |  |  |  | 33 | my $rng = @args ? | 
| 3127 |  |  |  |  |  |  | $self->__parse_distance( shift @args, '1pc' ) : | 
| 3128 |  |  |  |  |  |  | 10000 * PARSEC; | 
| 3129 | 1 | 50 |  |  |  | 6 | my $pmra = @args ? do { | 
| 3130 | 1 |  |  |  |  | 4 | my $angle = shift @args; | 
| 3131 | 1 | 50 |  |  |  | 16 | $angle =~ s/ s \z //smxi | 
| 3132 |  |  |  |  |  |  | or $angle *= 24 / 360 / cos( $ra ); | 
| 3133 | 1 |  |  |  |  | 25 | deg2rad( $angle / SPY2DPS ); | 
| 3134 |  |  |  |  |  |  | } : 0; | 
| 3135 | 1 | 50 |  |  |  | 33 | my $pmdec = @args ? deg2rad( shift( @args ) / SPY2DPS ) : 0; | 
| 3136 | 1 | 50 |  |  |  | 17 | my $pmrec = @args ? shift @args : 0; | 
| 3137 | 1 |  |  |  |  | 6 | push @{ $self->{sky} }, Astro::Coord::ECI::Star->new( | 
| 3138 |  |  |  |  |  |  | debug	=> $self->{debug}, | 
| 3139 | 1 |  |  |  |  | 3 | name	=> $name, | 
| 3140 |  |  |  |  |  |  | sun		=> $self->_sky_object( 'sun' ), | 
| 3141 |  |  |  |  |  |  | )->position( $ra, $dec, $rng, $pmra, $pmdec, $pmrec ); | 
| 3142 |  |  |  |  |  |  | } | 
| 3143 | 4 |  |  |  |  | 2116 | return; | 
| 3144 | 20 |  |  | 20 |  | 17476 | } | 
|  | 20 |  |  |  |  | 72 |  | 
|  | 20 |  |  |  |  | 95 |  | 
| 3145 |  |  |  |  |  |  |  | 
| 3146 |  |  |  |  |  |  | sub _sky_sub_class : Verb( add! delete! ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3147 | 0 |  |  | 0 |  | 0 | my ( $self, $opt, @arg ) = __arguments( @_ ); | 
| 3148 |  |  |  |  |  |  |  | 
| 3149 |  |  |  |  |  |  | $opt->{add} | 
| 3150 |  |  |  |  |  |  | and $opt->{delete} | 
| 3151 | 0 | 0 | 0 |  |  | 0 | and $self->wail( 'May not specify both add and delete' ); | 
| 3152 |  |  |  |  |  |  |  | 
| 3153 | 0 | 0 |  |  |  | 0 | if ( $opt->{delete} ) { | 
|  |  | 0 |  |  |  |  |  | 
| 3154 | 0 |  |  |  |  | 0 | foreach my $name ( @arg ) { | 
| 3155 | 0 | 0 |  |  |  | 0 | $name =~ m/ \A sun \z /smxi | 
| 3156 |  |  |  |  |  |  | and $self->wail( 'Can not remove Sun class' ); | 
| 3157 | 0 | 0 |  |  |  | 0 | defined $self->_find_in_sky( $name ) | 
| 3158 |  |  |  |  |  |  | and $self->wail( 'Can not remove in-use class' ); | 
| 3159 | 0 |  |  |  |  | 0 | delete $self->{sky_class}{ fold_case( $name ) }; | 
| 3160 |  |  |  |  |  |  | } | 
| 3161 |  |  |  |  |  |  | } elsif ( @arg < 2 ) { | 
| 3162 |  |  |  |  |  |  | @arg | 
| 3163 | 0 | 0 |  |  |  | 0 | or @arg = sort keys %{ $self->{sky_class} }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3164 |  |  |  |  |  |  | return join '', map { | 
| 3165 | 0 |  |  |  |  | 0 | $self->_sky_class_components( $_ ) . "\n" } | 
|  | 0 |  |  |  |  | 0 |  | 
| 3166 |  |  |  |  |  |  | @arg; | 
| 3167 |  |  |  |  |  |  | } else { | 
| 3168 | 0 |  |  |  |  | 0 | my ( $name, $class, @attr ) = @arg; | 
| 3169 | 0 |  |  |  |  | 0 | $self->load_package( { fatal => 'wail' }, $class ); | 
| 3170 | 0 | 0 |  |  |  | 0 | my $want_class = $name =~ m/ \A sun \z /smxi ? | 
| 3171 |  |  |  |  |  |  | SUN_CLASS_DEFAULT : | 
| 3172 |  |  |  |  |  |  | 'Astro::Coord::ECI'; | 
| 3173 | 0 | 0 |  |  |  | 0 | embodies( $class, $want_class ) | 
| 3174 |  |  |  |  |  |  | or $self->wail( | 
| 3175 |  |  |  |  |  |  | "Must be a subclass of $want_class" ); | 
| 3176 |  |  |  |  |  |  | +{ @attr }->{name} | 
| 3177 | 0 | 0 |  |  |  | 0 | and $self->wail( 'May not specify name explicitly' ); | 
| 3178 |  |  |  |  |  |  | # name must be last, because _sky_class_components() | 
| 3179 |  |  |  |  |  |  | # needs to recover it. | 
| 3180 | 0 |  |  |  |  | 0 | push @attr, name => $name; | 
| 3181 | 0 |  |  |  |  | 0 | my $obj = $class->new( @attr ); | 
| 3182 | 0 |  |  |  |  | 0 | my $folded_name = fold_case( $name ); | 
| 3183 | 0 |  |  |  |  | 0 | $self->{sky_class}{$folded_name} = [ $class, @attr ]; | 
| 3184 |  |  |  |  |  |  | $self->_replace_in_sky( $folded_name, $obj ) | 
| 3185 |  |  |  |  |  |  | or $opt->{add} | 
| 3186 | 0 | 0 | 0 |  |  | 0 | and push @{ $self->{sky} }, $obj; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3187 | 0 |  |  |  |  | 0 | $self->{_help_module}{$folded_name} = $class; | 
| 3188 | 0 | 0 |  |  |  | 0 | if ( $obj->isa( 'Astro::Coord::ECI::Sun' ) ) { | 
| 3189 | 0 |  |  |  |  | 0 | foreach my $body ( | 
| 3190 | 0 |  |  |  |  | 0 | @{ $self->{bodies} }, @{ $self->{sky} } | 
|  | 0 |  |  |  |  | 0 |  | 
| 3191 |  |  |  |  |  |  | ) { | 
| 3192 | 0 |  |  |  |  | 0 | $body->set( | 
| 3193 |  |  |  |  |  |  | sun => $self->_sky_object( 'sun' ), | 
| 3194 |  |  |  |  |  |  | ); | 
| 3195 |  |  |  |  |  |  | } | 
| 3196 |  |  |  |  |  |  | } | 
| 3197 |  |  |  |  |  |  | } | 
| 3198 |  |  |  |  |  |  |  | 
| 3199 | 0 |  |  |  |  | 0 | return; | 
| 3200 | 20 |  |  | 20 |  | 13598 | } | 
|  | 20 |  |  |  |  | 58 |  | 
|  | 20 |  |  |  |  | 114 |  | 
| 3201 |  |  |  |  |  |  |  | 
| 3202 |  |  |  |  |  |  | sub _sky_sub_clear : Verb() {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3203 | 1 |  |  | 1 |  | 5 | my ( $self ) = __arguments( @_ );	# $opt and args unused | 
| 3204 | 1 |  |  |  |  | 7 | @{ $self->{sky} } = (); | 
|  | 1 |  |  |  |  | 5 |  | 
| 3205 | 1 |  |  |  |  | 4 | return; | 
| 3206 | 20 |  |  | 20 |  | 5134 | } | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 115 |  | 
| 3207 |  |  |  |  |  |  |  | 
| 3208 |  |  |  |  |  |  | sub _sky_sub_drop : Verb() Tweak( -completion _sky_body_complete ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3209 | 1 |  |  | 1 |  | 21 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 3210 | 1 | 50 |  |  |  | 12 | @args or $self->wail( | 
| 3211 |  |  |  |  |  |  | 'You must specify at least one name to drop' ); | 
| 3212 | 1 |  |  |  |  | 19 | foreach my $name ( @args ) { | 
| 3213 | 1 |  |  |  |  | 6 | $self->_drop_from_sky( $name ); | 
| 3214 |  |  |  |  |  |  | } | 
| 3215 | 1 |  |  |  |  | 4 | return; | 
| 3216 | 20 |  |  | 20 |  | 5786 | } | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 93 |  | 
| 3217 |  |  |  |  |  |  |  | 
| 3218 |  |  |  |  |  |  | sub _sky_sub_list : Verb( verbose! ) {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3219 | 5 |  |  | 5 |  | 23 | my ( $self, $opt ) = __arguments( @_ );	# args unused | 
| 3220 | 5 |  |  |  |  | 38 | my $output; | 
| 3221 | 5 |  |  |  |  | 17 | foreach my $body ( | 
| 3222 | 8 |  |  |  |  | 47 | map { $_->[1] } | 
| 3223 | 4 |  |  |  |  | 71 | sort { $a->[0] cmp $b->[0] } | 
| 3224 | 8 |  | 33 |  |  | 158 | map { [ lc( $_->get( 'name' ) || $_->get( 'id' ) ), $_ ] } | 
| 3225 | 5 |  |  |  |  | 24 | @{$self->{sky}} | 
| 3226 |  |  |  |  |  |  | ) { | 
| 3227 | 8 |  |  |  |  | 31 | $output .= _sky_list_body( $body ); | 
| 3228 | 8 | 50 |  |  |  | 93 | if ( $opt->{verbose} ) { | 
| 3229 | 0 |  |  |  |  | 0 | $output .= "#   Class: @{[ ref $body ]}\n"; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3230 |  |  |  |  |  |  | } | 
| 3231 |  |  |  |  |  |  | } | 
| 3232 | 5 | 100 |  |  |  | 23 | unless (@{$self->{sky}}) { | 
|  | 5 |  |  |  |  | 27 |  | 
| 3233 |  |  |  |  |  |  | $self->{warn_on_empty} | 
| 3234 | 1 | 50 |  |  |  | 9 | and $self->whinge( 'The sky is empty' ); | 
| 3235 |  |  |  |  |  |  | } | 
| 3236 | 5 |  |  |  |  | 18 | return $output; | 
| 3237 | 20 |  |  | 20 |  | 9158 | } | 
|  | 20 |  |  |  |  | 47 |  | 
|  | 20 |  |  |  |  | 142 |  | 
| 3238 |  |  |  |  |  |  |  | 
| 3239 |  |  |  |  |  |  | # Undocumented. That means I can revoke at any time, without notice. If | 
| 3240 |  |  |  |  |  |  | # you need this functionality, please contact me. | 
| 3241 |  |  |  |  |  |  | sub _sky_sub_load : Verb() {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3242 | 0 |  |  | 0 |  | 0 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 3243 | 0 |  |  |  |  | 0 | my $tle; | 
| 3244 | 0 |  |  |  |  | 0 | foreach my $fn ( @args ) { | 
| 3245 | 0 |  |  |  |  | 0 | local $/ = undef; | 
| 3246 | 0 | 0 |  |  |  | 0 | open my $fh, '<', $fn | 
| 3247 |  |  |  |  |  |  | or $self->wail( "Failed to open $fn: $!" ); | 
| 3248 | 0 |  |  |  |  | 0 | $tle .= <$fh>; | 
| 3249 | 0 |  |  |  |  | 0 | close $fh; | 
| 3250 |  |  |  |  |  |  | } | 
| 3251 | 0 |  |  |  |  | 0 | return $self->_sky_sub_tle( $tle ); | 
| 3252 | 20 |  |  | 20 |  | 6595 | } | 
|  | 20 |  |  |  |  | 59 |  | 
|  | 20 |  |  |  |  | 138 |  | 
| 3253 |  |  |  |  |  |  |  | 
| 3254 |  |  |  |  |  |  | sub _sky_sub_lookup : Verb() {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 3255 | 0 |  |  | 0 |  | 0 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 3256 | 0 |  |  |  |  | 0 | my $output; | 
| 3257 | 0 |  |  |  |  | 0 | my $name = shift @args; | 
| 3258 | 0 | 0 |  |  |  | 0 | defined $self->_find_in_sky( $name ) | 
| 3259 |  |  |  |  |  |  | and $self->wail( "Duplicate sky entry '$name'" ); | 
| 3260 | 0 |  |  |  |  | 0 | my ($ra, $dec, $rng, $pmra, $pmdec, $pmrec) = | 
| 3261 |  |  |  |  |  |  | $self->_simbad4 ($name); | 
| 3262 | 0 |  |  |  |  | 0 | $rng = sprintf '%.2f', $rng; | 
| 3263 | 0 |  |  |  |  | 0 | $output .= 'sky add ' . quoter ($name) . | 
| 3264 |  |  |  |  |  |  | " $ra $dec $rng $pmra $pmdec $pmrec\n"; | 
| 3265 | 0 |  |  |  |  | 0 | $ra = deg2rad ($self->__parse_angle ($ra)); | 
| 3266 | 0 |  |  |  |  | 0 | my $body = Astro::Coord::ECI::Star->new( | 
| 3267 |  |  |  |  |  |  | name	=> $name, | 
| 3268 |  |  |  |  |  |  | sun	=> $self->_sky_object( 'sun' ), | 
| 3269 |  |  |  |  |  |  | ); | 
| 3270 | 0 |  |  |  |  | 0 | $body->position ($ra, deg2rad ($self->__parse_angle ($dec)), | 
| 3271 |  |  |  |  |  |  | $rng * PARSEC, deg2rad ($pmra * 24 / 360 / cos ($ra) / SPY2DPS), | 
| 3272 |  |  |  |  |  |  | deg2rad ($pmdec / SPY2DPS), $pmrec); | 
| 3273 | 0 |  |  |  |  | 0 | push @{$self->{sky}}, $body; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3274 | 0 |  |  |  |  | 0 | return $output; | 
| 3275 | 20 |  |  | 20 |  | 9131 | } | 
|  | 20 |  |  |  |  | 67 |  | 
|  | 20 |  |  |  |  | 137 |  | 
| 3276 |  |  |  |  |  |  |  | 
| 3277 |  |  |  |  |  |  | # Undocumented. That means I can revoke at any time, without notice. If | 
| 3278 |  |  |  |  |  |  | # you need this functionality, please contact me. | 
| 3279 |  |  |  |  |  |  | sub _sky_sub_tle : Verb() { | 
| 3280 | 0 |  |  | 0 |  | 0 | my ( $self, undef, $tle ) = __arguments( @_ );	# $opt unused | 
| 3281 | 0 |  |  |  |  | 0 | my @bodies = Astro::Coord::ECI::TLE::Set->aggregate( | 
| 3282 |  |  |  |  |  |  | Astro::Coord::ECI::TLE->parse( $tle ) ); | 
| 3283 | 0 |  |  |  |  | 0 | my %extant = map { $_->get( 'id' ) => 1 } | 
| 3284 | 0 |  |  |  |  | 0 | grep { embodies( $_, 'Astro::Coord::ECI::TLE' ) } | 
| 3285 | 0 |  |  |  |  | 0 | @{ $self->{sky} }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3286 | 0 |  |  |  |  | 0 | foreach my $body ( @bodies ) { | 
| 3287 | 0 |  |  |  |  | 0 | my $id = $body->get( 'id' ); | 
| 3288 | 0 | 0 |  |  |  | 0 | $extant{$id} | 
| 3289 |  |  |  |  |  |  | and $self->wail( "Duplicate sky entry $id" ); | 
| 3290 |  |  |  |  |  |  | } | 
| 3291 | 0 |  |  |  |  | 0 | push @{ $self->{sky} }, @bodies; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3292 | 0 |  |  |  |  | 0 | return sprintf "sky tle %s\n", quoter( $tle ); | 
| 3293 | 20 |  |  | 20 |  | 8237 | } | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 127 |  | 
| 3294 |  |  |  |  |  |  |  | 
| 3295 |  |  |  |  |  |  | sub source : Verb( optional! ) { | 
| 3296 | 8 |  |  | 8 | 1 | 34 | my ( $self, $opt, $src, @args ) = __arguments( @_ ); | 
| 3297 |  |  |  |  |  |  |  | 
| 3298 | 8 |  |  |  |  | 20 | my $output; | 
| 3299 | 8 | 100 |  |  |  | 45 | my $reader = $self->_file_reader( $src, $opt ) | 
| 3300 |  |  |  |  |  |  | or return; | 
| 3301 |  |  |  |  |  |  |  | 
| 3302 | 6 |  |  |  |  | 16 | my @level1_cache; | 
| 3303 | 6 |  |  |  |  | 19 | my $level1_context = {}; | 
| 3304 |  |  |  |  |  |  | my $fetcher = $opt->{level1} ? sub { | 
| 3305 |  |  |  |  |  |  | @level1_cache | 
| 3306 | 21 | 100 |  | 21 |  | 50 | and return shift @level1_cache; | 
| 3307 | 19 |  |  |  |  | 36 | my $buffer = $reader->(); | 
| 3308 | 19 |  |  |  |  | 72 | @level1_cache = $self->_rewrite_level1_command( | 
| 3309 |  |  |  |  |  |  | $buffer, $level1_context ); | 
| 3310 | 19 |  |  |  |  | 60 | return shift @level1_cache; | 
| 3311 | 6 | 100 |  |  |  | 45 | } : $reader; | 
| 3312 |  |  |  |  |  |  |  | 
| 3313 | 6 |  |  |  |  | 30 | my $frames = $self->_frame_push( source => \@args ); | 
| 3314 |  |  |  |  |  |  | # Note that level1 is unsupported, and works only when the | 
| 3315 |  |  |  |  |  |  | # options are passed as a hash. It will go away when support for | 
| 3316 |  |  |  |  |  |  | # the original satpass script is dropped. | 
| 3317 | 6 |  |  |  |  | 24 | $self->{frame}[-1]{level1} = $opt->{level1}; | 
| 3318 | 6 |  |  |  |  | 14 | my $err; | 
| 3319 | 6 | 50 |  |  |  | 12 | my $ok = eval { while ( defined( my $input =  $fetcher->() ) ) { | 
|  | 6 |  |  |  |  | 22 |  | 
| 3320 | 13 | 100 |  |  |  | 82 | if ( defined ( my $buffer = $self->execute( $fetcher, | 
| 3321 |  |  |  |  |  |  | $input ) ) ) { | 
| 3322 | 2 |  |  |  |  | 9 | $output .= $buffer; | 
| 3323 |  |  |  |  |  |  | } | 
| 3324 |  |  |  |  |  |  | } | 
| 3325 | 6 |  |  |  |  | 36 | 1; | 
| 3326 |  |  |  |  |  |  | } or $err = $@; | 
| 3327 |  |  |  |  |  |  |  | 
| 3328 | 6 |  |  |  |  | 38 | $self->_frame_pop( $frames ); | 
| 3329 | 6 | 50 |  |  |  | 21 | $ok or $self->whinge( $err ); | 
| 3330 |  |  |  |  |  |  |  | 
| 3331 | 6 | 100 |  |  |  | 39 | $opt->{level1} and $self->_rewrite_level1_macros(); | 
| 3332 | 6 |  |  |  |  | 101 | return $output; | 
| 3333 | 20 |  |  | 20 |  | 9950 | } | 
|  | 20 |  |  |  |  | 64 |  | 
|  | 20 |  |  |  |  | 113 |  | 
| 3334 |  |  |  |  |  |  |  | 
| 3335 |  |  |  |  |  |  | { | 
| 3336 |  |  |  |  |  |  |  | 
| 3337 |  |  |  |  |  |  | my %handler = ( | 
| 3338 |  |  |  |  |  |  | config	=> sub { | 
| 3339 |  |  |  |  |  |  | my ( $self, $obj, undef, $opt, @args ) = @_;	# $method unused | 
| 3340 |  |  |  |  |  |  | @args or @args = $obj->attribute_names(); | 
| 3341 |  |  |  |  |  |  | my ( $rslt, @values, $virgin ); | 
| 3342 |  |  |  |  |  |  | $opt->{changes} | 
| 3343 |  |  |  |  |  |  | and $virgin = $self->_get_spacetrack_default(); | 
| 3344 |  |  |  |  |  |  | foreach my $name ( @args ) { | 
| 3345 |  |  |  |  |  |  | $rslt = $obj->get( $name ); | 
| 3346 |  |  |  |  |  |  | $rslt->is_success() | 
| 3347 |  |  |  |  |  |  | or return $rslt; | 
| 3348 |  |  |  |  |  |  | my $value = $rslt->content(); | 
| 3349 | 20 |  |  | 20 |  | 6050 | no warnings qw{ uninitialized }; | 
|  | 20 |  |  |  |  | 61 |  | 
|  | 20 |  |  |  |  | 16884 |  | 
| 3350 |  |  |  |  |  |  | $opt->{changes} | 
| 3351 |  |  |  |  |  |  | and $value eq $virgin->getv( $name ) | 
| 3352 |  |  |  |  |  |  | and next; | 
| 3353 |  |  |  |  |  |  | push @values, [ $name, $value ]; | 
| 3354 |  |  |  |  |  |  | } | 
| 3355 |  |  |  |  |  |  | if ( $opt->{raw} ) { | 
| 3356 |  |  |  |  |  |  | $rslt->content( \@values ); | 
| 3357 |  |  |  |  |  |  | } else { | 
| 3358 |  |  |  |  |  |  | $opt->{raw} and return \@values; | 
| 3359 |  |  |  |  |  |  | my $output = ''; | 
| 3360 |  |  |  |  |  |  | foreach ( @values ) { | 
| 3361 |  |  |  |  |  |  | $output .= quoter( qw{ spacetrack set }, @{ $_ } ) . "\n"; | 
| 3362 |  |  |  |  |  |  | } | 
| 3363 |  |  |  |  |  |  | $rslt->content( $output ); | 
| 3364 |  |  |  |  |  |  | } | 
| 3365 |  |  |  |  |  |  | return $rslt; | 
| 3366 |  |  |  |  |  |  | }, | 
| 3367 |  |  |  |  |  |  | get	=> sub { | 
| 3368 |  |  |  |  |  |  | my ( undef, $obj, undef, $opt, @args ) = @_;	# Invocant, $method unused | 
| 3369 |  |  |  |  |  |  | my $rslt = $obj->get( @args ); | 
| 3370 |  |  |  |  |  |  | $rslt->is_success | 
| 3371 |  |  |  |  |  |  | and not $opt->{raw} | 
| 3372 |  |  |  |  |  |  | and $rslt->content( scalar quoter( | 
| 3373 |  |  |  |  |  |  | qw{ spacetrack set }, $args[0], $rslt->content() ) ); | 
| 3374 |  |  |  |  |  |  | return $rslt; | 
| 3375 |  |  |  |  |  |  | }, | 
| 3376 |  |  |  |  |  |  | set	=> sub { | 
| 3377 |  |  |  |  |  |  | my ( undef, $obj, $method, undef, @args ) = @_;	# Invocant, $opt unused | 
| 3378 |  |  |  |  |  |  | return $obj->$method( @args ); | 
| 3379 |  |  |  |  |  |  | }, | 
| 3380 |  |  |  |  |  |  | ); | 
| 3381 |  |  |  |  |  |  | $handler{getv} = $handler{get}; | 
| 3382 |  |  |  |  |  |  | $handler{show} = $handler{config}; | 
| 3383 |  |  |  |  |  |  | $handler{spacetrack_query_v2} = $handler{set}; | 
| 3384 |  |  |  |  |  |  |  | 
| 3385 |  |  |  |  |  |  | my %suppress_output = map { $_ => 1 } '', 'set'; | 
| 3386 |  |  |  |  |  |  |  | 
| 3387 |  |  |  |  |  |  | # Attributes must all be on one line to process correctly under | 
| 3388 |  |  |  |  |  |  | # 5.8.8. | 
| 3389 |  |  |  |  |  |  | sub spacetrack : Verb( all! changes! descending! effective! end_epoch=s exclude=s last5! raw! rcs! status=s sort=s start_epoch=s tle! verbose! ) { | 
| 3390 | 0 |  |  | 0 | 1 | 0 | my ( $self, $opt, $method, @args ) = __arguments( @_ ); | 
| 3391 |  |  |  |  |  |  |  | 
| 3392 |  |  |  |  |  |  | exists $opt->{raw} | 
| 3393 | 0 | 0 |  |  |  | 0 | or $opt->{raw} = ( ! _is_interactive() ); | 
| 3394 |  |  |  |  |  |  |  | 
| 3395 | 0 |  |  |  |  | 0 | my $verbose = delete $opt->{verbose}; | 
| 3396 |  |  |  |  |  |  |  | 
| 3397 | 0 |  |  |  |  | 0 | my $object = $self->_helper_get_object( 'spacetrack' ); | 
| 3398 |  |  |  |  |  |  | $method !~ m/ \A _ /smx and $object->can( $method ) | 
| 3399 | 0 | 0 | 0 |  |  | 0 | or $handler{$method} | 
|  |  |  | 0 |  |  |  |  | 
| 3400 |  |  |  |  |  |  | or $self->wail("No such spacetrack method as '$method'"); | 
| 3401 |  |  |  |  |  |  |  | 
| 3402 |  |  |  |  |  |  | $opt->{start_epoch} | 
| 3403 |  |  |  |  |  |  | and $opt->{start_epoch} = $self->__parse_time( | 
| 3404 | 0 | 0 |  |  |  | 0 | $opt->{start_epoch} ); | 
| 3405 |  |  |  |  |  |  | $opt->{end_epoch} | 
| 3406 |  |  |  |  |  |  | and $opt->{end_epoch} = $self->__parse_time( | 
| 3407 | 0 | 0 |  |  |  | 0 | $opt->{end_epoch} ); | 
| 3408 |  |  |  |  |  |  |  | 
| 3409 | 0 |  |  |  |  | 0 | my ( $rslt, @rest ); | 
| 3410 | 0 | 0 |  |  |  | 0 | if ( $handler{$method} ) { | 
| 3411 | 0 |  |  |  |  | 0 | ( $rslt, @rest ) = $handler{$method}->( | 
| 3412 |  |  |  |  |  |  | $self, $object, $method, $opt, @args ); | 
| 3413 |  |  |  |  |  |  | } else { | 
| 3414 | 0 |  |  |  |  | 0 | delete $opt->{raw}; | 
| 3415 | 0 |  |  |  |  | 0 | ( $rslt, @rest ) = $object->$method( $opt, @args ); | 
| 3416 |  |  |  |  |  |  | } | 
| 3417 |  |  |  |  |  |  |  | 
| 3418 | 0 | 0 |  |  |  | 0 | $rslt->is_success() | 
| 3419 |  |  |  |  |  |  | or $self->wail( $rslt->status_line() ); | 
| 3420 |  |  |  |  |  |  |  | 
| 3421 | 0 |  |  |  |  | 0 | my $output; | 
| 3422 | 0 |  | 0 |  |  | 0 | my $content_type = $object->content_type || ''; | 
| 3423 |  |  |  |  |  |  |  | 
| 3424 | 0 | 0 | 0 |  |  | 0 | if ($content_type eq 'orbit') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 3425 |  |  |  |  |  |  |  | 
| 3426 | 0 |  |  |  |  | 0 | push @{$self->{bodies}}, | 
|  | 0 |  |  |  |  | 0 |  | 
| 3427 |  |  |  |  |  |  | Astro::Coord::ECI::TLE->parse ($rslt->content); | 
| 3428 | 0 | 0 |  |  |  | 0 | $verbose | 
| 3429 |  |  |  |  |  |  | and $output .= $rslt->content; | 
| 3430 |  |  |  |  |  |  |  | 
| 3431 |  |  |  |  |  |  | } elsif ($content_type eq 'iridium-status') { | 
| 3432 |  |  |  |  |  |  |  | 
| 3433 | 0 |  |  |  |  | 0 | $self->_iridium_status( @rest ); | 
| 3434 | 0 | 0 |  |  |  | 0 | $verbose | 
| 3435 |  |  |  |  |  |  | and $output .= $rslt->content; | 
| 3436 |  |  |  |  |  |  |  | 
| 3437 |  |  |  |  |  |  | } elsif ( ! $suppress_output{$content_type} || $verbose ) { | 
| 3438 |  |  |  |  |  |  |  | 
| 3439 | 0 |  |  |  |  | 0 | $output .= $rslt->content; | 
| 3440 |  |  |  |  |  |  |  | 
| 3441 |  |  |  |  |  |  | } | 
| 3442 |  |  |  |  |  |  |  | 
| 3443 | 0 | 0 |  |  |  | 0 | defined $output | 
| 3444 |  |  |  |  |  |  | and $output =~ s/ (? | 
| 3445 | 0 |  |  |  |  | 0 | return $output; | 
| 3446 | 20 |  |  | 20 |  | 184 | } | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 132 |  | 
| 3447 |  |  |  |  |  |  |  | 
| 3448 |  |  |  |  |  |  | } | 
| 3449 |  |  |  |  |  |  |  | 
| 3450 |  |  |  |  |  |  | sub st : Verb() { | 
| 3451 | 0 |  |  | 0 | 1 | 0 | my ( $self, undef, $func, @args ) = __arguments( @_ );	# $opt unused | 
| 3452 |  |  |  |  |  |  |  | 
| 3453 | 0 |  |  |  |  | 0 | $self->_deprecation_notice( method => 'st' ); | 
| 3454 | 0 | 0 |  |  |  | 0 | if ( 'localize' eq $func ) { | 
| 3455 | 0 |  |  |  |  | 0 | my $st = $self->_helper_get_object( 'spacetrack' ); | 
| 3456 | 0 |  |  |  |  | 0 | foreach my $key (@args) { | 
| 3457 |  |  |  |  |  |  | exists $self->{frame}[-1]{spacetrack}{$key} | 
| 3458 | 0 | 0 |  |  |  | 0 | or $self->{frame}[-1]{spacetrack}{$key} = | 
| 3459 |  |  |  |  |  |  | $st->get ($key)->content | 
| 3460 |  |  |  |  |  |  | } | 
| 3461 |  |  |  |  |  |  | } else { | 
| 3462 | 0 |  |  |  |  | 0 | goto &spacetrack; | 
| 3463 |  |  |  |  |  |  | } | 
| 3464 | 0 |  |  |  |  | 0 | return; | 
| 3465 | 20 |  |  | 20 |  | 7557 | } | 
|  | 20 |  |  |  |  | 60 |  | 
|  | 20 |  |  |  |  | 88 |  | 
| 3466 |  |  |  |  |  |  |  | 
| 3467 |  |  |  |  |  |  | sub station { | 
| 3468 | 34 |  |  | 34 | 1 | 151 | my ( $self ) = @_; | 
| 3469 |  |  |  |  |  |  |  | 
| 3470 |  |  |  |  |  |  | defined $self->{height} | 
| 3471 |  |  |  |  |  |  | and defined $self->{latitude} | 
| 3472 |  |  |  |  |  |  | and defined $self->{longitude} | 
| 3473 | 34 | 50 | 33 |  |  | 409 | or $self->wail( 'You must set height, latitude, and longitude' ); | 
|  |  |  | 33 |  |  |  |  | 
| 3474 |  |  |  |  |  |  |  | 
| 3475 |  |  |  |  |  |  | return Astro::Coord::ECI->new ( | 
| 3476 |  |  |  |  |  |  | almanac_horizon	=> $self->{_almanac_horizon}, | 
| 3477 |  |  |  |  |  |  | horizon	=> deg2rad( $self->get( 'horizon' ) ), | 
| 3478 |  |  |  |  |  |  | id		=> 'station', | 
| 3479 |  |  |  |  |  |  | name	=> $self->{location} || '', | 
| 3480 |  |  |  |  |  |  | refraction	=> $self->{refraction} || 0, | 
| 3481 |  |  |  |  |  |  | )->geodetic ( | 
| 3482 |  |  |  |  |  |  | deg2rad( $self->{latitude} ), | 
| 3483 |  |  |  |  |  |  | deg2rad( $self->{longitude} ), | 
| 3484 | 34 |  | 100 |  |  | 171 | $self->{height} / 1000 | 
|  |  |  | 50 |  |  |  |  | 
| 3485 |  |  |  |  |  |  | ); | 
| 3486 |  |  |  |  |  |  | } | 
| 3487 |  |  |  |  |  |  |  | 
| 3488 |  |  |  |  |  |  | # TODO I must have thought -reload would be good for something, but it | 
| 3489 |  |  |  |  |  |  | # appears I never implemented it. | 
| 3490 |  |  |  |  |  |  |  | 
| 3491 |  |  |  |  |  |  | sub status : Verb( name! reload! ) { | 
| 3492 | 3 |  |  | 3 | 1 | 21 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 3493 |  |  |  |  |  |  |  | 
| 3494 | 3 | 100 |  |  |  | 34 | @args or @args = qw{show}; | 
| 3495 |  |  |  |  |  |  |  | 
| 3496 | 3 |  | 50 |  |  | 21 | my $verb = lc (shift (@args) || 'show'); | 
| 3497 |  |  |  |  |  |  |  | 
| 3498 | 3 | 50 |  |  |  | 14 | if ( $verb eq 'iridium' ) { | 
| 3499 | 0 |  |  |  |  | 0 | $self->_deprecation_notice( status => 'iridium', 'show' ); | 
| 3500 | 0 |  |  |  |  | 0 | $verb = 'show'; | 
| 3501 |  |  |  |  |  |  | } | 
| 3502 |  |  |  |  |  |  |  | 
| 3503 | 3 |  |  |  |  | 5 | my $output; | 
| 3504 |  |  |  |  |  |  |  | 
| 3505 | 3 | 100 | 66 |  |  | 49 | if ($verb eq 'add' || $verb eq 'drop') { | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 3506 |  |  |  |  |  |  |  | 
| 3507 | 1 |  |  |  |  | 21 | Astro::Coord::ECI::TLE->status ($verb, @args); | 
| 3508 | 1 |  |  |  |  | 24 | foreach my $tle (@{$self->{bodies}}) { | 
|  | 1 |  |  |  |  | 5 |  | 
| 3509 | 1 | 50 |  |  |  | 4 | $tle->get ('id') == $args[0] and $tle->rebless (); | 
| 3510 |  |  |  |  |  |  | } | 
| 3511 |  |  |  |  |  |  |  | 
| 3512 |  |  |  |  |  |  | } elsif ($verb eq 'clear') { | 
| 3513 |  |  |  |  |  |  |  | 
| 3514 | 1 |  |  |  |  | 12 | Astro::Coord::ECI::TLE->status ($verb, @args); | 
| 3515 | 1 |  |  |  |  | 20 | foreach my $tle (@{$self->{bodies}}) { | 
|  | 1 |  |  |  |  | 9 |  | 
| 3516 | 2 |  |  |  |  | 137 | $tle->rebless (); | 
| 3517 |  |  |  |  |  |  | } | 
| 3518 |  |  |  |  |  |  |  | 
| 3519 |  |  |  |  |  |  | } elsif ($verb eq 'show' || $verb eq 'list') { | 
| 3520 |  |  |  |  |  |  |  | 
| 3521 | 1 |  |  |  |  | 8 | my @data = Astro::Coord::ECI::TLE->status( 'show', @args ); | 
| 3522 | 1 | 50 |  |  |  | 19 | @data = sort {$a->[3] cmp $b->[3]} @data if $opt->{name}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3523 | 1 |  |  |  |  | 5 | $output .= '';	# Don't want it to be undef. | 
| 3524 |  |  |  |  |  |  |  | 
| 3525 |  |  |  |  |  |  | my $encoder = ( HAVE_TLE_IRIDIUM && | 
| 3526 |  |  |  |  |  |  | Astro::Coord::ECI::TLE::Iridium->can( | 
| 3527 | 1 |  |  | 0 |  | 18 | '__encode_operational_status' ) ) || sub { return $_[2] }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3528 |  |  |  |  |  |  |  | 
| 3529 | 1 |  |  |  |  | 12 | foreach my $tle (@data) { | 
| 3530 | 0 |  |  |  |  | 0 | my $status = $encoder->( undef, status => $tle->[2] ); | 
| 3531 | 0 |  |  |  |  | 0 | $output .= quoter( 'status', 'add', | 
| 3532 |  |  |  |  |  |  | $tle->[0], $tle->[1], $status, | 
| 3533 |  |  |  |  |  |  | $tle->[3], $tle->[4] ) . "\n"; | 
| 3534 |  |  |  |  |  |  | } | 
| 3535 |  |  |  |  |  |  |  | 
| 3536 |  |  |  |  |  |  | } else { | 
| 3537 | 0 |  |  |  |  | 0 | $output .= '';	# Don't want it to be undef. | 
| 3538 | 0 |  |  |  |  | 0 | $output .= Astro::Coord::ECI::TLE->status ($verb, @args); | 
| 3539 |  |  |  |  |  |  | } | 
| 3540 |  |  |  |  |  |  |  | 
| 3541 | 3 |  |  |  |  | 246 | return $output; | 
| 3542 |  |  |  |  |  |  |  | 
| 3543 | 20 |  |  | 20 |  | 15295 | } | 
|  | 20 |  |  |  |  | 44 |  | 
|  | 20 |  |  |  |  | 138 |  | 
| 3544 |  |  |  |  |  |  |  | 
| 3545 |  |  |  |  |  |  | sub system : method Verb() {	## no critic (ProhibitBuiltInHomonyms) | 
| 3546 | 4 |  |  | 4 | 1 | 23 | my ( $self, undef, $verb, @args ) = __arguments( @_ );	# $opt unused | 
| 3547 |  |  |  |  |  |  |  | 
| 3548 |  |  |  |  |  |  | @args = map { | 
| 3549 | 4 |  |  |  |  | 22 | bsd_glob( $_, GLOB_NOCHECK | GLOB_BRACE | GLOB_QUOTE ) | 
|  | 8 |  |  |  |  | 285 |  | 
| 3550 |  |  |  |  |  |  | } @args; | 
| 3551 | 4 |  |  |  |  | 22 | my $stdout = $self->{frame}[-1]{localout}; | 
| 3552 | 4 |  |  |  |  | 11 | my @exported = keys %{ $self->{exported} }; | 
|  | 4 |  |  |  |  | 32 |  | 
| 3553 | 4 |  |  |  |  | 13 | local @ENV{@exported} = map { $mutator{$_} ? $self->get( $_ ) : | 
| 3554 | 5 | 100 |  |  |  | 61 | $self->{exported}{$_} } @exported; | 
| 3555 | 4 | 50 | 33 |  |  | 53 | if ( defined $stdout && -t $stdout ) { | 
| 3556 | 0 |  |  |  |  | 0 | CORE::system {$verb} $verb, @args; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3557 | 0 |  |  |  |  | 0 | return; | 
| 3558 |  |  |  |  |  |  | } else { | 
| 3559 | 4 |  |  |  |  | 77 | $self->load_package( { fatal => 'wail' }, 'IPC::System::Simple' ); | 
| 3560 | 4 |  |  |  |  | 20 | return IPC::System::Simple::capturex( $verb, @args ); | 
| 3561 |  |  |  |  |  |  | } | 
| 3562 | 20 |  |  | 20 |  | 9028 | } | 
|  | 20 |  |  |  |  | 79 |  | 
|  | 20 |  |  |  |  | 116 |  | 
| 3563 |  |  |  |  |  |  |  | 
| 3564 |  |  |  |  |  |  | sub time : method Verb() Tweak( -unsatisfied ) {	## no critic (ProhibitBuiltInHomonyms,RequireArgUnpacking) | 
| 3565 | 1 | 50 |  | 1 | 1 | 5 | my ($self, @args) = map { ARRAY_REF eq ref $_ ? @{ $_ } : $_ } @_; | 
|  | 2 |  |  |  |  | 18 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 3566 | 1 | 50 |  |  |  | 11 | $have_time_hires->() or $self->wail( 'Time::HiRes not available' ); | 
| 3567 | 1 |  |  |  |  | 14 | $self->_dispatch_check( time => $args[0] ); | 
| 3568 | 1 |  |  |  |  | 5 | my $start = Time::HiRes::time(); | 
| 3569 |  |  |  |  |  |  | # If we're inside an unsatisfied if() we do not do the timing, | 
| 3570 |  |  |  |  |  |  | # because dispatch() is probably a no-op. | 
| 3571 |  |  |  |  |  |  | $self->_in_unsatisfied_if() | 
| 3572 |  |  |  |  |  |  | or $self->_add_post_dispatch( | 
| 3573 |  |  |  |  |  |  | sub { | 
| 3574 | 1 |  |  | 1 |  | 5 | return sprintf "%.3f seconds\n", Time::HiRes::time() - $start; | 
| 3575 |  |  |  |  |  |  | }, | 
| 3576 | 1 | 50 |  |  |  | 10 | ); | 
| 3577 | 1 |  |  |  |  | 13 | return $self->dispatch( @args ); | 
| 3578 | 20 |  |  | 20 |  | 7721 | } | 
|  | 20 |  |  |  |  | 52 |  | 
|  | 20 |  |  |  |  | 120 |  | 
| 3579 |  |  |  |  |  |  |  | 
| 3580 |  |  |  |  |  |  | sub time_parser : Verb() { | 
| 3581 | 0 | 0 |  | 0 | 1 | 0 | splice @_, ( HASH_REF eq ref $_[1] ? 2 : 1 ), 0, 'time_parser'; | 
| 3582 | 0 |  |  |  |  | 0 | goto &_helper_handler; | 
| 3583 | 20 |  |  | 20 |  | 6216 | } | 
|  | 20 |  |  |  |  | 83 |  | 
|  | 20 |  |  |  |  | 124 |  | 
| 3584 |  |  |  |  |  |  |  | 
| 3585 |  |  |  |  |  |  | sub tle : Verb( :compute __tle_options ) { | 
| 3586 | 4 |  |  | 4 | 1 | 18 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 3587 |  |  |  |  |  |  | @args | 
| 3588 |  |  |  |  |  |  | and not $opt->{choose} | 
| 3589 | 4 | 50 | 33 |  |  | 19 | and $opt->{choose} = \@args; | 
| 3590 |  |  |  |  |  |  |  | 
| 3591 | 4 |  |  |  |  | 19 | my $bodies = $self->__choose( $opt->{choose}, $self->{bodies} ); | 
| 3592 | 4 |  |  |  |  | 10 | @{ $bodies } = map { $_->[0] } | 
|  | 5 |  |  |  |  | 133 |  | 
| 3593 | 1 | 50 |  |  |  | 54 | sort { $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] } | 
| 3594 | 5 |  |  |  |  | 48 | map { [ $_, $_->get( 'id' ), $_->get( 'epoch' ) ] } | 
| 3595 | 4 |  |  |  |  | 11 | @{ $bodies }; | 
|  | 4 |  |  |  |  | 10 |  | 
| 3596 | 4 |  |  |  |  | 13 | my $tplt_name = delete $opt->{_template}; | 
| 3597 | 4 |  |  |  |  | 14 | return $self->__format_data( $tplt_name => $bodies, $opt ); | 
| 3598 | 20 |  |  | 20 |  | 7908 | } | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 117 |  | 
| 3599 |  |  |  |  |  |  |  | 
| 3600 |  |  |  |  |  |  | sub __tle_options { | 
| 3601 | 4 |  |  | 4 |  | 11 | my ( $self, $opt ) = @_; | 
| 3602 |  |  |  |  |  |  | return [ | 
| 3603 | 4 |  |  |  |  | 29 | qw{ choose=s@ }, | 
| 3604 |  |  |  |  |  |  | $self->_templates_to_options( tle => $opt ), | 
| 3605 |  |  |  |  |  |  | ]; | 
| 3606 |  |  |  |  |  |  | } | 
| 3607 |  |  |  |  |  |  |  | 
| 3608 |  |  |  |  |  |  | sub unexport : Verb() { | 
| 3609 | 1 |  |  | 1 | 1 | 7 | my ( $self, undef, @args ) = __arguments( @_ );	# $opt unused | 
| 3610 |  |  |  |  |  |  |  | 
| 3611 | 1 |  |  |  |  | 20 | foreach my $name ( @args ) { | 
| 3612 | 1 |  |  |  |  | 7 | delete $self->{exported}{$name}; | 
| 3613 |  |  |  |  |  |  | } | 
| 3614 | 1 |  |  |  |  | 4 | return; | 
| 3615 | 20 |  |  | 20 |  | 6563 | } | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 102 |  | 
| 3616 |  |  |  |  |  |  |  | 
| 3617 |  |  |  |  |  |  | sub validate : Verb( quiet! ) { | 
| 3618 | 1 |  |  | 1 | 1 | 7 | my ( $self, $opt, @args ) = __arguments( @_ ); | 
| 3619 |  |  |  |  |  |  |  | 
| 3620 | 1 |  |  |  |  | 13 | my $pass_start = $self->__parse_time ( | 
| 3621 |  |  |  |  |  |  | shift @args, $self->_get_day_noon()); | 
| 3622 | 1 |  | 50 |  |  | 21 | my $pass_end = $self->__parse_time (shift @args || '+7'); | 
| 3623 | 1 | 50 |  |  |  | 5 | $pass_start >= $pass_end | 
| 3624 |  |  |  |  |  |  | and $self->wail( 'End time must be after start time' ); | 
| 3625 |  |  |  |  |  |  |  | 
| 3626 | 1 | 50 |  |  |  | 3 | @{ $self->{bodies} } | 
|  | 1 |  |  |  |  | 5 |  | 
| 3627 |  |  |  |  |  |  | or $self->wail( 'No bodies selected' ); | 
| 3628 |  |  |  |  |  |  |  | 
| 3629 |  |  |  |  |  |  | #	Validate each body. | 
| 3630 |  |  |  |  |  |  |  | 
| 3631 | 1 |  |  |  |  | 7 | my @valid; | 
| 3632 | 1 |  |  |  |  | 14 | foreach my $tle ( $self->_aggregate( $self->{bodies} ) ) { | 
| 3633 | 2 | 100 |  |  |  | 1750 | $tle->validate( $opt, $pass_start, $pass_end ) | 
| 3634 |  |  |  |  |  |  | and push @valid, $tle->members(); | 
| 3635 |  |  |  |  |  |  | } | 
| 3636 |  |  |  |  |  |  |  | 
| 3637 | 1 |  |  |  |  | 725 | $self->{bodies} = \@valid; | 
| 3638 |  |  |  |  |  |  |  | 
| 3639 | 1 |  |  |  |  | 4 | return; | 
| 3640 | 20 |  |  | 20 |  | 7691 | } | 
|  | 20 |  |  |  |  | 46 |  | 
|  | 20 |  |  |  |  | 105 |  | 
| 3641 |  |  |  |  |  |  |  | 
| 3642 |  |  |  |  |  |  | sub version : Verb() { | 
| 3643 | 0 |  |  | 0 | 1 | 0 | return <<"EOD"; | 
| 3644 |  |  |  |  |  |  |  | 
| 3645 | 0 |  |  |  |  | 0 | @{[__PACKAGE__]} $VERSION - Satellite pass predictor | 
| 3646 | 0 |  |  |  |  | 0 | based on Astro::Coord::ECI @{[Astro::Coord::ECI->VERSION]} | 
| 3647 |  |  |  |  |  |  | Copyright (C) 2009-2023 by Thomas R. Wyant, III | 
| 3648 |  |  |  |  |  |  |  | 
| 3649 |  |  |  |  |  |  | EOD | 
| 3650 | 20 |  |  | 20 |  | 5318 | } | 
|  | 20 |  |  |  |  | 78 |  | 
|  | 20 |  |  |  |  | 107 |  | 
| 3651 |  |  |  |  |  |  |  | 
| 3652 |  |  |  |  |  |  | ######################################################################## | 
| 3653 |  |  |  |  |  |  |  | 
| 3654 |  |  |  |  |  |  | #   $self->_add_post_dispatch( $code_ref ); | 
| 3655 |  |  |  |  |  |  |  | 
| 3656 |  |  |  |  |  |  | #   Add a reference to code to be executed after the current interactive | 
| 3657 |  |  |  |  |  |  | #   method is dispatched. All such code is executed, in the reverse of | 
| 3658 |  |  |  |  |  |  | #   the order it was added. The only argument will be the invocant. | 
| 3659 |  |  |  |  |  |  | #   Because it is added to the current execution frame, if the | 
| 3660 |  |  |  |  |  |  | #   interactive method being dispatched is begin(), the code will be | 
| 3661 |  |  |  |  |  |  | #   executed after the corresponding end(). Code to make the execution | 
| 3662 |  |  |  |  |  |  | #   happen is, of course, in dispatch(). | 
| 3663 |  |  |  |  |  |  | sub _add_post_dispatch { | 
| 3664 | 23 |  |  | 23 |  | 51 | my ( $self, $code ) = @_; | 
| 3665 | 23 |  | 50 |  |  | 40 | push @{ $self->{frame}[-1]{post_dispatch} ||= [] }, $code; | 
|  | 23 |  |  |  |  | 119 |  | 
| 3666 | 23 |  |  |  |  | 45 | return; | 
| 3667 |  |  |  |  |  |  | } | 
| 3668 |  |  |  |  |  |  |  | 
| 3669 |  |  |  |  |  |  | #	$self->_aggregate( $list_ref ); | 
| 3670 |  |  |  |  |  |  |  | 
| 3671 |  |  |  |  |  |  | sub __add_to_observing_list { | 
| 3672 | 5 |  |  | 5 |  | 14541 | my ( $self, @args ) = @_; | 
| 3673 | 5 |  |  |  |  | 30 | foreach my $body ( @args ) { | 
| 3674 | 10 | 50 |  |  |  | 213 | embodies( $body, 'Astro::Coord::ECI::TLE' ) | 
| 3675 |  |  |  |  |  |  | and next; | 
| 3676 | 0 |  |  |  |  | 0 | my $id = $body->get( 'id' ); | 
| 3677 | 0 | 0 |  |  |  | 0 | defined $id | 
| 3678 |  |  |  |  |  |  | or $id = $body->get( 'name' ); | 
| 3679 | 0 |  |  |  |  | 0 | $self->wail( "Body $id is not a TLE" ); | 
| 3680 |  |  |  |  |  |  | } | 
| 3681 | 5 |  |  |  |  | 110 | push @{ $self->{bodies} }, @args; | 
|  | 5 |  |  |  |  | 29 |  | 
| 3682 | 5 |  |  |  |  | 31 | return $self; | 
| 3683 |  |  |  |  |  |  | } | 
| 3684 |  |  |  |  |  |  |  | 
| 3685 |  |  |  |  |  |  | #	This is just a wrapper for | 
| 3686 |  |  |  |  |  |  | #	Astro::Coord::ECI::TLE::Set->aggregate. | 
| 3687 |  |  |  |  |  |  |  | 
| 3688 |  |  |  |  |  |  | sub _aggregate { | 
| 3689 | 27 |  |  | 27 |  | 108 | my ( $self, $bodies ) = @_; | 
| 3690 | 27 |  |  |  |  | 121 | local $Astro::Coord::ECI::TLE::Set::Singleton = $self->{singleton}; | 
| 3691 | 27 |  |  |  |  | 72 | return Astro::Coord::ECI::TLE::Set->aggregate ( @{ $bodies } ); | 
|  | 27 |  |  |  |  | 316 |  | 
| 3692 |  |  |  |  |  |  | } | 
| 3693 |  |  |  |  |  |  |  | 
| 3694 |  |  |  |  |  |  | #	_apply_boolean_default( \%opt, $invert, @keys ); | 
| 3695 |  |  |  |  |  |  | # | 
| 3696 |  |  |  |  |  |  | #	This subroutine defaults a set of boolean options. The keys in | 
| 3697 |  |  |  |  |  |  | #	the set are specified in @keys, and the defined values are | 
| 3698 |  |  |  |  |  |  | #	inverted before the defaults are applied if $invert is true. | 
| 3699 |  |  |  |  |  |  | #	Nothing is returned. | 
| 3700 |  |  |  |  |  |  |  | 
| 3701 |  |  |  |  |  |  | sub _apply_boolean_default { | 
| 3702 | 44 |  |  | 44 |  | 197 | my ( $self, $opt, $invert, @keys ) = @_; | 
| 3703 | 44 |  |  |  |  | 122 | my $state = my $found = 0; | 
| 3704 | 44 |  |  |  |  | 107 | foreach my $key ( @keys ) { | 
| 3705 | 136 | 100 |  |  |  | 331 | if ( exists $opt->{$key} ) { | 
| 3706 | 8 |  |  |  |  | 24 | $found++; | 
| 3707 |  |  |  |  |  |  | $invert | 
| 3708 | 8 | 50 |  |  |  | 35 | and $opt->{$key} = ( !  $opt->{$key} ); | 
| 3709 | 8 | 100 |  |  |  | 40 | $state |= ( $opt->{$key} ? 2 : 1 ); | 
| 3710 |  |  |  |  |  |  | } | 
| 3711 |  |  |  |  |  |  | } | 
| 3712 |  |  |  |  |  |  | 1 == $state			# Only negated options found | 
| 3713 |  |  |  |  |  |  | and @keys == $found	# All options in group were specified | 
| 3714 |  |  |  |  |  |  | and $self->wail( 'May not negate all of ' . join ', ', map { | 
| 3715 | 44 | 50 | 66 |  |  | 234 | "-$_" } @keys ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 3716 | 44 |  |  |  |  | 142 | my $default = $state < 2; | 
| 3717 | 44 |  |  |  |  | 121 | foreach my $key ( @keys ) { | 
| 3718 |  |  |  |  |  |  | exists $opt->{$key} | 
| 3719 | 136 | 100 |  |  |  | 421 | or $opt->{$key} = $default; | 
| 3720 |  |  |  |  |  |  | } | 
| 3721 | 44 |  |  |  |  | 130 | return; | 
| 3722 |  |  |  |  |  |  | } | 
| 3723 |  |  |  |  |  |  |  | 
| 3724 |  |  |  |  |  |  | #	$self->_attribute_exists( $name, %arg ); | 
| 3725 |  |  |  |  |  |  | # | 
| 3726 |  |  |  |  |  |  | #	This method returns true if an accessor for the given attribute | 
| 3727 |  |  |  |  |  |  | #	exists, and croaks otherwise. | 
| 3728 |  |  |  |  |  |  | #	Attributes in the %level1_attr hash fail unless in level1 mode | 
| 3729 |  |  |  |  |  |  | #	Named arguments: | 
| 3730 |  |  |  |  |  |  | #	  query: if true, returns false if attribute does not exist | 
| 3731 |  |  |  |  |  |  |  | 
| 3732 |  |  |  |  |  |  | { | 
| 3733 |  |  |  |  |  |  | my %level1_attr = map { $_ => 1 } qw{ sun }; | 
| 3734 |  |  |  |  |  |  |  | 
| 3735 |  |  |  |  |  |  | sub _attribute_exists { | 
| 3736 | 1280 |  |  | 1280 |  | 2876 | my ( $self, $name, %arg ) = @_; | 
| 3737 |  |  |  |  |  |  | exists $accessor{$name} | 
| 3738 |  |  |  |  |  |  | and ( ! $level1_attr{$name} || $self->{frame}[-1]{level1} ) | 
| 3739 | 1280 | 50 | 33 |  |  | 7191 | and return $accessor{$name}; | 
|  |  |  | 33 |  |  |  |  | 
| 3740 |  |  |  |  |  |  | $arg{query} | 
| 3741 | 0 | 0 |  |  |  | 0 | or $self->wail("No such attribute as '$name'"); | 
| 3742 | 0 |  |  |  |  | 0 | return; | 
| 3743 |  |  |  |  |  |  | } | 
| 3744 |  |  |  |  |  |  | } | 
| 3745 |  |  |  |  |  |  |  | 
| 3746 |  |  |  |  |  |  | { | 
| 3747 |  |  |  |  |  |  |  | 
| 3748 |  |  |  |  |  |  | my %spacetrack_attributes; | 
| 3749 |  |  |  |  |  |  | $have_astro_spacetrack->() | 
| 3750 |  |  |  |  |  |  | and %spacetrack_attributes = map { $_ => 1 } | 
| 3751 |  |  |  |  |  |  | Astro::SpaceTrack->attribute_names(); | 
| 3752 |  |  |  |  |  |  |  | 
| 3753 |  |  |  |  |  |  | my %special = ( | 
| 3754 |  |  |  |  |  |  | formatter	=> sub { | 
| 3755 |  |  |  |  |  |  | my ( $obj, $attr ) = @_; | 
| 3756 |  |  |  |  |  |  | $obj->can( $attr ) | 
| 3757 |  |  |  |  |  |  | or return NULL; | 
| 3758 |  |  |  |  |  |  | return $obj->$attr(); | 
| 3759 |  |  |  |  |  |  | }, | 
| 3760 |  |  |  |  |  |  | spacetrack	=> sub { | 
| 3761 |  |  |  |  |  |  | my ( $obj, $attr ) = @_; | 
| 3762 |  |  |  |  |  |  | $spacetrack_attributes{$attr} | 
| 3763 |  |  |  |  |  |  | or return NULL; | 
| 3764 |  |  |  |  |  |  | return $obj->getv( $attr ); | 
| 3765 |  |  |  |  |  |  | }, | 
| 3766 |  |  |  |  |  |  | time_parser	=> sub { | 
| 3767 |  |  |  |  |  |  | my ( $obj, $attr ) = @_; | 
| 3768 |  |  |  |  |  |  | $obj->can( $attr ) | 
| 3769 |  |  |  |  |  |  | or return NULL; | 
| 3770 |  |  |  |  |  |  | return $obj->$attr(); | 
| 3771 |  |  |  |  |  |  | }, | 
| 3772 |  |  |  |  |  |  | ); | 
| 3773 |  |  |  |  |  |  |  | 
| 3774 |  |  |  |  |  |  | # my $value = $self->_attribute_value( $name ); | 
| 3775 |  |  |  |  |  |  | # | 
| 3776 |  |  |  |  |  |  | # Return an attribute value. If the attribute is 'formatter', | 
| 3777 |  |  |  |  |  |  | # 'spacetrack' or 'time_parser' you can specify a dot and the name | 
| 3778 |  |  |  |  |  |  | # of an attribute of the relevant object, e.g. spacetrack.username. | 
| 3779 |  |  |  |  |  |  | # If the attribute does not exist you get back manifest constant | 
| 3780 |  |  |  |  |  |  | # NULL, which is a reference to undef blessed into class 'Null'. | 
| 3781 |  |  |  |  |  |  | sub _attribute_value { | 
| 3782 | 43 |  |  | 43 |  | 88 | my ( $self, $name ) = @_; | 
| 3783 | 43 |  |  |  |  | 272 | my ( $attr, $sub ) = split qr{ [.] }smx, $name, 2; | 
| 3784 | 43 | 100 |  |  |  | 183 | $accessor{$attr} | 
| 3785 |  |  |  |  |  |  | or return NULL; | 
| 3786 | 9 |  |  |  |  | 25 | my $rslt = $self->get( $attr ); | 
| 3787 | 9 | 100 |  |  |  | 26 | if ( defined $sub ) { | 
| 3788 |  |  |  |  |  |  | $rslt | 
| 3789 | 2 | 50 | 33 |  |  | 40 | and my $code = $special{$attr} | 
| 3790 |  |  |  |  |  |  | or return NULL; | 
| 3791 | 2 |  |  |  |  | 12 | $rslt = $code->( $rslt, $sub ); | 
| 3792 |  |  |  |  |  |  | } | 
| 3793 | 9 |  |  |  |  | 22 | return $rslt; | 
| 3794 |  |  |  |  |  |  | } | 
| 3795 |  |  |  |  |  |  | } | 
| 3796 |  |  |  |  |  |  |  | 
| 3797 |  |  |  |  |  |  | # Documented in POD | 
| 3798 |  |  |  |  |  |  |  | 
| 3799 |  |  |  |  |  |  | { | 
| 3800 |  |  |  |  |  |  | my %chooser = ( | 
| 3801 |  |  |  |  |  |  | ''	=> sub { | 
| 3802 |  |  |  |  |  |  | my ( $sel ) = @_; | 
| 3803 |  |  |  |  |  |  | my @rslt; | 
| 3804 |  |  |  |  |  |  | foreach my $s ( split qr{ \s* , \s* }smx, $sel ) { | 
| 3805 |  |  |  |  |  |  | if ( $s =~ m/ \D /smx || $s < 1000 ) { | 
| 3806 |  |  |  |  |  |  | my $re = qr{\Q$s\E}i; | 
| 3807 |  |  |  |  |  |  | push @rslt, sub { | 
| 3808 |  |  |  |  |  |  | my ( $tle, $context ) = @_; | 
| 3809 |  |  |  |  |  |  | $context->{name} ||= $tle->get( 'name' ); | 
| 3810 |  |  |  |  |  |  | defined $context->{name} | 
| 3811 |  |  |  |  |  |  | or return; | 
| 3812 |  |  |  |  |  |  | return $context->{name} =~ $re; | 
| 3813 |  |  |  |  |  |  | }; | 
| 3814 |  |  |  |  |  |  | } else { | 
| 3815 |  |  |  |  |  |  | push @rslt, sub { | 
| 3816 |  |  |  |  |  |  | my ( $tle, $context ) = @_; | 
| 3817 |  |  |  |  |  |  | $context->{id} ||= $tle->get( 'id' ); | 
| 3818 |  |  |  |  |  |  | return $context->{id} == $s; | 
| 3819 |  |  |  |  |  |  | }; | 
| 3820 |  |  |  |  |  |  | } | 
| 3821 |  |  |  |  |  |  | } | 
| 3822 |  |  |  |  |  |  | return @rslt; | 
| 3823 |  |  |  |  |  |  | }, | 
| 3824 |  |  |  |  |  |  | CODE_REF()	=> sub { | 
| 3825 |  |  |  |  |  |  | my ( $sel ) = @_; | 
| 3826 |  |  |  |  |  |  | return $sel; | 
| 3827 |  |  |  |  |  |  | }, | 
| 3828 |  |  |  |  |  |  | REGEXP_REF()	=> sub { | 
| 3829 |  |  |  |  |  |  | my ( $sel ) = @_; | 
| 3830 |  |  |  |  |  |  | return sub { | 
| 3831 |  |  |  |  |  |  | my ( $tle, $context ) = @_; | 
| 3832 |  |  |  |  |  |  | $context->{name} ||= $tle->get( 'name' ); | 
| 3833 |  |  |  |  |  |  | return $context->{name} =~ $sel; | 
| 3834 |  |  |  |  |  |  | }; | 
| 3835 |  |  |  |  |  |  | }, | 
| 3836 |  |  |  |  |  |  | ); | 
| 3837 |  |  |  |  |  |  |  | 
| 3838 |  |  |  |  |  |  | sub __choose { | 
| 3839 | 45 |  |  | 45 |  | 285 | my ( $self, @args ) = @_; | 
| 3840 | 45 | 100 |  |  |  | 201 | my $opt = HASH_REF eq ref $args[0] ? shift @args : {}; | 
| 3841 | 45 |  |  |  |  | 107 | my $choice = shift @args; | 
| 3842 | 45 | 100 |  |  |  | 148 | defined $choice | 
| 3843 |  |  |  |  |  |  | or $choice = []; | 
| 3844 | 45 | 50 |  |  |  | 190 | ARRAY_REF eq ref $choice | 
| 3845 |  |  |  |  |  |  | or $self->weep( 'Choice invalid' ); | 
| 3846 | 45 |  |  |  |  | 130 | my @rslt; | 
| 3847 |  |  |  |  |  |  | my @selector; | 
| 3848 | 45 |  |  |  |  | 83 | foreach my $sel ( @{ $choice } ) { | 
|  | 45 |  |  |  |  | 182 |  | 
| 3849 | 5 |  |  |  |  | 12 | my $ref = ref $sel; | 
| 3850 | 5 | 50 |  |  |  | 41 | my $code = $chooser{$ref} | 
| 3851 |  |  |  |  |  |  | or $self->weep( "$ref not supported as chooser" ); | 
| 3852 | 5 |  |  |  |  | 28 | push @selector, $code->( $sel ); | 
| 3853 |  |  |  |  |  |  | } | 
| 3854 |  |  |  |  |  |  |  | 
| 3855 |  |  |  |  |  |  | $opt->{bodies} | 
| 3856 |  |  |  |  |  |  | and push @args, | 
| 3857 | 45 | 100 |  |  |  | 188 | $self->_aggregate( $self->{bodies} ); | 
| 3858 |  |  |  |  |  |  | $opt->{sky} | 
| 3859 | 45 | 100 |  |  |  | 849 | and push @args, $self->{sky}; | 
| 3860 |  |  |  |  |  |  |  | 
| 3861 | 45 | 100 |  |  |  | 129 | @args = map { ARRAY_REF eq ref $_ ? @{ $_ } : $_ } @args; | 
|  | 51 |  |  |  |  | 174 |  | 
|  | 43 |  |  |  |  | 198 |  | 
| 3862 |  |  |  |  |  |  |  | 
| 3863 |  |  |  |  |  |  | not @selector | 
| 3864 | 45 | 100 |  |  |  | 367 | and return wantarray ? @args : \@args; | 
|  |  | 100 |  |  |  |  |  | 
| 3865 |  |  |  |  |  |  |  | 
| 3866 | 5 |  |  |  |  | 14 | foreach my $tle ( @args ) { | 
| 3867 | 10 | 50 |  |  |  | 41 | ARRAY_REF eq ref $tle | 
| 3868 |  |  |  |  |  |  | and $self->weep( 'Schwartzian-transform objects not supported' ); | 
| 3869 |  |  |  |  |  |  |  | 
| 3870 | 10 |  |  |  |  | 18 | my $match = $opt->{invert}; | 
| 3871 | 10 |  |  |  |  | 18 | my $context = {}; | 
| 3872 | 10 |  |  |  |  | 21 | foreach my $sel ( @selector ) { | 
| 3873 | 10 | 100 |  |  |  | 21 | $sel->( $tle, $context ) | 
| 3874 |  |  |  |  |  |  | or next; | 
| 3875 | 4 |  |  |  |  | 12 | $match = !$match; | 
| 3876 | 4 |  |  |  |  | 8 | last; | 
| 3877 |  |  |  |  |  |  | } | 
| 3878 |  |  |  |  |  |  |  | 
| 3879 | 10 | 100 |  |  |  | 36 | $match and push @rslt, $tle; | 
| 3880 |  |  |  |  |  |  | } | 
| 3881 |  |  |  |  |  |  |  | 
| 3882 | 5 | 100 |  |  |  | 53 | return wantarray ? @rslt : \@rslt; | 
| 3883 |  |  |  |  |  |  | } | 
| 3884 |  |  |  |  |  |  |  | 
| 3885 |  |  |  |  |  |  | } | 
| 3886 |  |  |  |  |  |  |  | 
| 3887 |  |  |  |  |  |  | #	$self->_deprecation_notice( $type, $name ); | 
| 3888 |  |  |  |  |  |  | # | 
| 3889 |  |  |  |  |  |  | #	This method centralizes deprecation. Type is 'attribute' or | 
| 3890 |  |  |  |  |  |  | #	'method'. Deprecation is driven of the %deprecate hash. Values | 
| 3891 |  |  |  |  |  |  | #	are: | 
| 3892 |  |  |  |  |  |  | #	    false - no warning | 
| 3893 |  |  |  |  |  |  | #	    1 - warn on first use | 
| 3894 |  |  |  |  |  |  | #	    2 - warn on each use | 
| 3895 |  |  |  |  |  |  | #	    3 - die on each use. | 
| 3896 |  |  |  |  |  |  | # | 
| 3897 |  |  |  |  |  |  | #	$self->_deprecation_in_progress( $type, $name ) | 
| 3898 |  |  |  |  |  |  | # | 
| 3899 |  |  |  |  |  |  | #	This method returns true if the deprecation is in progress. In | 
| 3900 |  |  |  |  |  |  | #	fact it returns the deprecation level. | 
| 3901 |  |  |  |  |  |  |  | 
| 3902 |  |  |  |  |  |  | { | 
| 3903 |  |  |  |  |  |  |  | 
| 3904 |  |  |  |  |  |  | my %deprecate = ( | 
| 3905 |  |  |  |  |  |  | attribute => { | 
| 3906 |  |  |  |  |  |  | country	=> 0, | 
| 3907 |  |  |  |  |  |  | date_format	=> 0, | 
| 3908 |  |  |  |  |  |  | desired_equinox_dynamical	=> 0, | 
| 3909 |  |  |  |  |  |  | explicit_macro_delete	=> 0, | 
| 3910 |  |  |  |  |  |  | gmt		=> 0, | 
| 3911 |  |  |  |  |  |  | local_coord	=> 0, | 
| 3912 |  |  |  |  |  |  | perltime	=> 0, | 
| 3913 |  |  |  |  |  |  | time_format	=> 0, | 
| 3914 |  |  |  |  |  |  | tz		=> 0, | 
| 3915 |  |  |  |  |  |  | }, | 
| 3916 |  |  |  |  |  |  | method => { | 
| 3917 |  |  |  |  |  |  | st		=> 0, | 
| 3918 |  |  |  |  |  |  | }, | 
| 3919 |  |  |  |  |  |  | status	=> { | 
| 3920 |  |  |  |  |  |  | iridium	=> 3, | 
| 3921 |  |  |  |  |  |  | }, | 
| 3922 |  |  |  |  |  |  | ); | 
| 3923 |  |  |  |  |  |  |  | 
| 3924 |  |  |  |  |  |  | sub _deprecation_notice { | 
| 3925 | 1277 |  |  | 1277 |  | 2857 | my ( $self, $type, $name, $repl ) = @_; | 
| 3926 | 1277 | 50 |  |  |  | 3346 | $deprecate{$type} or return; | 
| 3927 | 1277 | 50 |  |  |  | 3408 | $deprecate{$type}{$name} or return; | 
| 3928 |  |  |  |  |  |  | my $msg = sprintf 'The %s %s is %s', $name, $type, | 
| 3929 | 0 | 0 |  |  |  | 0 | $deprecate{$type}{$name} > 2 ? 'removed' : 'deprecated'; | 
| 3930 | 0 | 0 |  |  |  | 0 | defined $repl | 
| 3931 |  |  |  |  |  |  | and $msg .= "; use $repl instead"; | 
| 3932 | 0 | 0 |  |  |  | 0 | $deprecate{$type}{$name} >= 3 | 
| 3933 |  |  |  |  |  |  | and $self->wail( $msg ); | 
| 3934 | 0 | 0 |  |  |  | 0 | warnings::enabled( 'deprecated' ) | 
| 3935 |  |  |  |  |  |  | and $self->whinge( $msg ); | 
| 3936 |  |  |  |  |  |  | $deprecate{$type}{$name} == 1 | 
| 3937 | 0 | 0 |  |  |  | 0 | and $deprecate{$type}{$name} = 0; | 
| 3938 | 0 |  |  |  |  | 0 | return; | 
| 3939 |  |  |  |  |  |  | } | 
| 3940 |  |  |  |  |  |  |  | 
| 3941 |  |  |  |  |  |  | sub _deprecation_in_progress { | 
| 3942 | 0 |  |  | 0 |  | 0 | my ( undef, $type, $name ) = @_;	# Invocant unused | 
| 3943 | 0 | 0 |  |  |  | 0 | $deprecate{$type} or return; | 
| 3944 | 0 |  |  |  |  | 0 | return $deprecate{$type}{$name}; | 
| 3945 |  |  |  |  |  |  | } | 
| 3946 |  |  |  |  |  |  |  | 
| 3947 |  |  |  |  |  |  | } | 
| 3948 |  |  |  |  |  |  |  | 
| 3949 |  |  |  |  |  |  | # my ( $obj ) = $self->_drop_from_sky( $name ); | 
| 3950 |  |  |  |  |  |  | # The return is an array containing the dropped body, or nothing if the | 
| 3951 |  |  |  |  |  |  | # body was not found. | 
| 3952 |  |  |  |  |  |  | sub _drop_from_sky { | 
| 3953 | 1 |  |  | 1 |  | 5 | my ( $self, $name ) = @_; | 
| 3954 | 1 | 50 |  |  |  | 15 | defined( my $inx = $self->_find_in_sky( $name ) ) | 
| 3955 |  |  |  |  |  |  | or return; | 
| 3956 | 1 |  |  |  |  | 25 | return splice @{ $self->{sky} }, $inx, 1; | 
|  | 1 |  |  |  |  | 4 |  | 
| 3957 |  |  |  |  |  |  | } | 
| 3958 |  |  |  |  |  |  |  | 
| 3959 |  |  |  |  |  |  | #	$fh = $self->_file_opener( $name, $mode ); | 
| 3960 |  |  |  |  |  |  | # | 
| 3961 |  |  |  |  |  |  | #	This method opens the given file, returning the handle. If the | 
| 3962 |  |  |  |  |  |  | #	mode is output, the current value of output_layers is appended. | 
| 3963 |  |  |  |  |  |  | #	An exception is thrown if the file can not be opened. | 
| 3964 |  |  |  |  |  |  |  | 
| 3965 |  |  |  |  |  |  | sub _file_opener { | 
| 3966 | 1 |  |  | 1 |  | 4 | my ( $self, $name, $mode ) = @_; | 
| 3967 |  |  |  |  |  |  |  | 
| 3968 |  |  |  |  |  |  | # NOTE special case for &1 (stdout) and &2 (stderr). | 
| 3969 |  |  |  |  |  |  | my $fh = ( $name =~ m/ \A & ( [12] ) \z /smx ) ? | 
| 3970 |  |  |  |  |  |  | [ | 
| 3971 |  |  |  |  |  |  | undef, | 
| 3972 | 1 | 50 | 0 |  |  | 12 | $self->{frame}[-1]{localout} || \*STDOUT, | 
|  |  | 50 |  |  |  |  |  | 
| 3973 |  |  |  |  |  |  | \*STDERR, | 
| 3974 |  |  |  |  |  |  | ]->[ $1 ] : | 
| 3975 |  |  |  |  |  |  | IO::File->new( $name, $mode ) | 
| 3976 |  |  |  |  |  |  | or $self->wail( "Unable to open $name: $!" ); | 
| 3977 |  |  |  |  |  |  |  | 
| 3978 | 1 | 50 |  |  |  | 237 | if ( $mode =~ m/ \A (?: [+>] | [|] - ) /smx ) { | 
| 3979 |  |  |  |  |  |  |  | 
| 3980 | 1 |  |  |  |  | 15 | my $layers = $self->get( 'output_layers' ); | 
| 3981 | 1 | 50 | 33 |  |  | 18 | if ( defined $layers && '' ne $layers ) { | 
| 3982 | 1 | 50 |  |  |  | 18 | binmode $fh, $layers | 
| 3983 |  |  |  |  |  |  | or $self->wail( | 
| 3984 |  |  |  |  |  |  | "Unable to set '$layers' on $name: $!" ); | 
| 3985 |  |  |  |  |  |  | } | 
| 3986 |  |  |  |  |  |  | } | 
| 3987 |  |  |  |  |  |  |  | 
| 3988 | 1 |  |  |  |  | 77 | return $fh; | 
| 3989 |  |  |  |  |  |  | } | 
| 3990 |  |  |  |  |  |  |  | 
| 3991 |  |  |  |  |  |  | #	$code = $self->_file_reader( $file, \%opt ); | 
| 3992 |  |  |  |  |  |  | # | 
| 3993 |  |  |  |  |  |  | #	This method returns a code snippet that returns the contents of | 
| 3994 |  |  |  |  |  |  | #	the file one line at a time. The $file can be any of: | 
| 3995 |  |  |  |  |  |  | # | 
| 3996 |  |  |  |  |  |  | #	* An open handle | 
| 3997 |  |  |  |  |  |  | #	* A URL (if LWP::UserAgent can be loaded) | 
| 3998 |  |  |  |  |  |  | #	* A file name | 
| 3999 |  |  |  |  |  |  | #	* A scalar reference | 
| 4000 |  |  |  |  |  |  | #	* An array reference | 
| 4001 |  |  |  |  |  |  | #	* A code reference, which is returned unmodified | 
| 4002 |  |  |  |  |  |  | # | 
| 4003 |  |  |  |  |  |  | #	The code snippet will return undef at end-of-file. | 
| 4004 |  |  |  |  |  |  | # | 
| 4005 |  |  |  |  |  |  | #	The following keys in %opt are recognized: | 
| 4006 |  |  |  |  |  |  | #	{encoding} specifies the encoding of the file. How this is used | 
| 4007 |  |  |  |  |  |  | #	    on the $file argument as follows: | 
| 4008 |  |  |  |  |  |  | #	    * An open handle -- unused | 
| 4009 |  |  |  |  |  |  | #	    * A URL ----------- unused (encoding taken from HTTP::Response) | 
| 4010 |  |  |  |  |  |  | #	    * A file name ----- used (default is utf-8) | 
| 4011 |  |  |  |  |  |  | #	    * A scalar ref ---- used (default is un-encoded) | 
| 4012 |  |  |  |  |  |  | #	    * An array ref ---- unused | 
| 4013 |  |  |  |  |  |  | #	    * A code ref ------ unused | 
| 4014 |  |  |  |  |  |  | #	{glob} causes the contents of the file to be returned, rather | 
| 4015 |  |  |  |  |  |  | #	    than a reader. | 
| 4016 |  |  |  |  |  |  | #	{optional} causes the code to simply return on an error, rather | 
| 4017 |  |  |  |  |  |  | #	    than failing. | 
| 4018 |  |  |  |  |  |  |  | 
| 4019 |  |  |  |  |  |  | sub _file_reader { | 
| 4020 | 25 |  |  | 25 |  | 3716 | my ( $self, $file, $opt ) = @_; | 
| 4021 |  |  |  |  |  |  |  | 
| 4022 | 25 | 100 |  |  |  | 119 | if ( openhandle( $file ) ) { | 
| 4023 |  |  |  |  |  |  | $opt->{glob} | 
| 4024 | 2 | 100 |  | 1 |  | 20 | or return sub { return scalar <$file> }; | 
|  | 1 |  |  |  |  | 22 |  | 
| 4025 | 1 |  |  |  |  | 13 | local $/ = undef; | 
| 4026 | 1 |  |  |  |  | 31 | return scalar <$file>; | 
| 4027 |  |  |  |  |  |  | } | 
| 4028 |  |  |  |  |  |  |  | 
| 4029 | 23 |  |  |  |  | 52 | my $ref = ref $file; | 
| 4030 | 23 | 50 |  |  |  | 165 | my $code = $self->can( "_file_reader_$ref" ) | 
| 4031 |  |  |  |  |  |  | or $self->wail( sprintf "Opening a $ref ref is unsupported" ); | 
| 4032 |  |  |  |  |  |  |  | 
| 4033 | 23 |  |  |  |  | 143 | goto &$code; | 
| 4034 |  |  |  |  |  |  | } | 
| 4035 |  |  |  |  |  |  |  | 
| 4036 |  |  |  |  |  |  | # Most of the following are called using '$self->can( | 
| 4037 |  |  |  |  |  |  | # "_file_reader_$ref" )', and there is no way a static analysis tool can | 
| 4038 |  |  |  |  |  |  | # find such calls. So we just have to exempt them from Perl::Critic | 
| 4039 |  |  |  |  |  |  |  | 
| 4040 |  |  |  |  |  |  | sub _file_reader_ {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4041 | 14 |  |  | 14 |  | 54 | my ( $self, $file, $opt ) = @_; | 
| 4042 |  |  |  |  |  |  |  | 
| 4043 | 14 | 50 |  |  |  | 85 | defined $file | 
| 4044 |  |  |  |  |  |  | and chomp $file; | 
| 4045 |  |  |  |  |  |  |  | 
| 4046 | 14 | 50 | 33 |  |  | 168 | if ( ! defined $file || ! ref $file &&  '' eq $file ) { | 
|  |  |  | 33 |  |  |  |  | 
| 4047 | 0 | 0 |  |  |  | 0 | $opt->{optional} and return; | 
| 4048 | 0 |  |  |  |  | 0 | $self->wail( 'Defined file required' ); | 
| 4049 |  |  |  |  |  |  | } | 
| 4050 |  |  |  |  |  |  |  | 
| 4051 | 14 | 50 |  |  |  | 95 | if ( $self->_file_reader__validate_url( $file ) ) { | 
| 4052 | 0 |  |  |  |  | 0 | my $ua = LWP::UserAgent->new(); | 
| 4053 | 0 |  |  |  |  | 0 | my $resp = $ua->get( $file ); | 
| 4054 |  |  |  |  |  |  | $resp->is_success() | 
| 4055 | 0 | 0 |  |  |  | 0 | or do { | 
| 4056 | 0 | 0 |  |  |  | 0 | $opt->{optional} and return; | 
| 4057 | 0 |  |  |  |  | 0 | $self->wail( "Failed to retrieve $file: ", | 
| 4058 |  |  |  |  |  |  | $resp->status_line() ); | 
| 4059 |  |  |  |  |  |  | }; | 
| 4060 | 0 | 0 |  |  |  | 0 | $opt->{glob} and return $resp->decoded_content(); | 
| 4061 | 0 |  |  |  |  | 0 | $opt = { %{ $opt }, encoding => $resp->content_charset() }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4062 | 0 |  |  |  |  | 0 | return $self->_file_reader( | 
| 4063 |  |  |  |  |  |  | \( scalar $resp->content() ), | 
| 4064 |  |  |  |  |  |  | $opt, | 
| 4065 |  |  |  |  |  |  | ); | 
| 4066 |  |  |  |  |  |  | } else { | 
| 4067 | 14 |  | 50 |  |  | 141 | my $encoding = $opt->{encoding} || 'utf-8'; | 
| 4068 |  |  |  |  |  |  | my $fh = IO::File->new( | 
| 4069 |  |  |  |  |  |  | $self->expand_tilde( $file ), | 
| 4070 |  |  |  |  |  |  | "<:encoding($encoding)", | 
| 4071 | 14 | 100 |  |  |  | 116 | ) or do { | 
| 4072 | 3 | 100 |  |  |  | 447 | $opt->{optional} and return; | 
| 4073 | 2 |  |  |  |  | 33 | $self->wail( "Failed to open $file: $!" ); | 
| 4074 |  |  |  |  |  |  | }; | 
| 4075 |  |  |  |  |  |  | $opt->{glob} | 
| 4076 | 11 | 100 |  | 16 |  | 3265 | or return sub { return scalar <$fh> }; | 
|  | 16 |  |  |  |  | 326 |  | 
| 4077 | 7 |  |  |  |  | 66 | local $/ = undef; | 
| 4078 | 7 |  |  |  |  | 370 | return scalar <$fh>; | 
| 4079 |  |  |  |  |  |  | } | 
| 4080 |  |  |  |  |  |  | } | 
| 4081 |  |  |  |  |  |  |  | 
| 4082 |  |  |  |  |  |  | sub _file_reader__validate_url { | 
| 4083 | 14 |  |  | 14 |  | 41 | my ( undef, $url ) = @_;		# Invocant unused | 
| 4084 |  |  |  |  |  |  |  | 
| 4085 | 14 | 50 |  |  |  | 92 | load_package( 'LWP::UserAgent' ) | 
| 4086 |  |  |  |  |  |  | or return; | 
| 4087 |  |  |  |  |  |  |  | 
| 4088 | 0 | 0 |  |  |  | 0 | load_package( 'URI' ) | 
| 4089 |  |  |  |  |  |  | or return; | 
| 4090 |  |  |  |  |  |  |  | 
| 4091 | 0 | 0 |  |  |  | 0 | load_package( 'LWP::Protocol' ) | 
| 4092 |  |  |  |  |  |  | or return; | 
| 4093 |  |  |  |  |  |  |  | 
| 4094 | 0 | 0 |  |  |  | 0 | my $obj = URI->new( $url ) | 
| 4095 |  |  |  |  |  |  | or return; | 
| 4096 | 0 | 0 |  |  |  | 0 | $obj->can( 'authority' ) | 
| 4097 |  |  |  |  |  |  | or return 1; | 
| 4098 |  |  |  |  |  |  |  | 
| 4099 | 0 | 0 |  |  |  | 0 | defined( my $scheme = $obj->scheme() ) | 
| 4100 |  |  |  |  |  |  | or return; | 
| 4101 | 0 | 0 |  |  |  | 0 | LWP::Protocol::implementor( $scheme ) | 
| 4102 |  |  |  |  |  |  | or return; | 
| 4103 |  |  |  |  |  |  |  | 
| 4104 | 0 |  |  |  |  | 0 | return 1; | 
| 4105 |  |  |  |  |  |  | } | 
| 4106 |  |  |  |  |  |  |  | 
| 4107 |  |  |  |  |  |  | sub _file_reader_ARRAY {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4108 | 5 |  |  | 5 |  | 14 | my ( undef, $file, $opt ) = @_;	# Invocant unused | 
| 4109 |  |  |  |  |  |  |  | 
| 4110 | 5 |  |  |  |  | 11 | my $inx = 0; | 
| 4111 |  |  |  |  |  |  | $opt->{glob} | 
| 4112 | 5 | 100 |  | 11 |  | 49 | or return sub { return $file->[$inx++] }; | 
|  | 11 |  |  |  |  | 28 |  | 
| 4113 | 1 |  |  |  |  | 10 | my $buffer; | 
| 4114 | 1 |  |  |  |  | 4 | foreach ( @{ $file } ) { | 
|  | 1 |  |  |  |  | 3 |  | 
| 4115 | 5 |  |  |  |  | 12 | $buffer .= $_; | 
| 4116 | 5 | 50 |  |  |  | 36 | $buffer =~ m/ \n \z /smx | 
| 4117 |  |  |  |  |  |  | or $buffer .= "\n"; | 
| 4118 |  |  |  |  |  |  | } | 
| 4119 | 1 |  |  |  |  | 7 | return $buffer; | 
| 4120 |  |  |  |  |  |  | } | 
| 4121 |  |  |  |  |  |  |  | 
| 4122 |  |  |  |  |  |  | sub _file_reader_CODE {		## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4123 | 2 |  |  | 2 |  | 15 | my ( undef, $file, $opt ) = @_;	# Invocant unused | 
| 4124 |  |  |  |  |  |  | $opt->{glob} | 
| 4125 | 2 | 100 |  |  |  | 9 | or return $file; | 
| 4126 | 1 |  |  |  |  | 5 | my $buffer; | 
| 4127 | 1 |  |  |  |  | 8 | local $_; | 
| 4128 | 1 |  |  |  |  | 47 | while ( defined( $_ = $file->() ) ) { | 
| 4129 | 5 |  |  |  |  | 31 | $buffer .= $_; | 
| 4130 | 5 | 50 |  |  |  | 23 | $buffer =~ m/ \n \z /smx | 
| 4131 |  |  |  |  |  |  | or $buffer .= "\n"; | 
| 4132 |  |  |  |  |  |  | } | 
| 4133 | 1 |  |  |  |  | 9 | return $buffer; | 
| 4134 |  |  |  |  |  |  | } | 
| 4135 |  |  |  |  |  |  |  | 
| 4136 |  |  |  |  |  |  | sub _file_reader_SCALAR {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4137 | 2 |  |  | 2 |  | 7 | my ( $self, $file, $opt ) = @_; | 
| 4138 |  |  |  |  |  |  |  | 
| 4139 |  |  |  |  |  |  | $opt->{glob} | 
| 4140 | 2 | 100 |  |  |  | 8 | and return ${ $file }; | 
|  | 1 |  |  |  |  | 6 |  | 
| 4141 | 1 | 50 |  |  |  | 6 | my $mode = $opt->{encoding} ? "<:encoding($opt->{encoding})" : '<'; | 
| 4142 |  |  |  |  |  |  |  | 
| 4143 | 1 | 50 |  |  |  | 7 | my $fh = IO::File->new( $file, $mode )	# Needs IO::File 1.14. | 
| 4144 |  |  |  |  |  |  | or $self->wail( "Failed to open SCALAR ref: $!" ); | 
| 4145 |  |  |  |  |  |  |  | 
| 4146 | 1 |  |  | 1 |  | 892 | return sub { return scalar <$fh> }; | 
|  | 1 |  |  |  |  | 8 |  | 
| 4147 |  |  |  |  |  |  | } | 
| 4148 |  |  |  |  |  |  |  | 
| 4149 |  |  |  |  |  |  | # $inx = $self->_find_in_sky( $name ) | 
| 4150 |  |  |  |  |  |  | # The return is the index of the named body in @{ $self->{sky} }, or | 
| 4151 |  |  |  |  |  |  | # undef if it is not present. 'Sun' and 'Moon' are special cases; | 
| 4152 |  |  |  |  |  |  | # everything else is presumed to be found by name. | 
| 4153 |  |  |  |  |  |  | sub _find_in_sky { | 
| 4154 | 6 |  |  | 6 |  | 17 | my ( $self, $name ) = @_; | 
| 4155 |  |  |  |  |  |  |  | 
| 4156 | 6 |  |  |  |  | 112 | my $re = qr/ \A \Q$name\E \z /smxi; | 
| 4157 | 6 |  |  |  |  | 12 | foreach my $inx ( 0 .. $#{ $self->{sky} } ) { | 
|  | 6 |  |  |  |  | 29 |  | 
| 4158 | 8 | 100 |  |  |  | 136 | $self->{sky}[$inx]->get( 'name' ) =~ $re | 
| 4159 |  |  |  |  |  |  | and return $inx; | 
| 4160 |  |  |  |  |  |  | } | 
| 4161 | 5 |  |  |  |  | 113 | return; | 
| 4162 |  |  |  |  |  |  | } | 
| 4163 |  |  |  |  |  |  |  | 
| 4164 |  |  |  |  |  |  | # Documented in POD | 
| 4165 |  |  |  |  |  |  |  | 
| 4166 |  |  |  |  |  |  | sub __format_data { | 
| 4167 | 41 |  |  | 41 |  | 4465 | my ( $self, $action, $data, $opt ) = @_; | 
| 4168 | 41 |  |  |  |  | 290 | return $self->_get_formatter_object( $opt )->format( | 
| 4169 |  |  |  |  |  |  | sp	=> $self, | 
| 4170 |  |  |  |  |  |  | template => $action, | 
| 4171 |  |  |  |  |  |  | data	=> $data, | 
| 4172 |  |  |  |  |  |  | opt	=> $opt, | 
| 4173 |  |  |  |  |  |  | ); | 
| 4174 |  |  |  |  |  |  | } | 
| 4175 |  |  |  |  |  |  |  | 
| 4176 |  |  |  |  |  |  | #	$frames = $satpass2->_frame_push($type, \@args); | 
| 4177 |  |  |  |  |  |  | # | 
| 4178 |  |  |  |  |  |  | #	This method pushes a context frame on the stack. The $type | 
| 4179 |  |  |  |  |  |  | #	describes the frame, and goes in the frame's {type} entry, but | 
| 4180 |  |  |  |  |  |  | #	is currently unused. The \@args entry goes in the {args} key, | 
| 4181 |  |  |  |  |  |  | #	and is the basis of argument expansion. The return is the number | 
| 4182 |  |  |  |  |  |  | #	of frames that were on the stack _BEFORE_ the now-current frame | 
| 4183 |  |  |  |  |  |  | #	was added to the stack. This gets passed to _frame_pop() to | 
| 4184 |  |  |  |  |  |  | #	restore the context stack to its status before the current frame | 
| 4185 |  |  |  |  |  |  | #	was added. | 
| 4186 |  |  |  |  |  |  |  | 
| 4187 |  |  |  |  |  |  | sub _frame_push { | 
| 4188 | 59 |  |  | 59 |  | 181 | my ( $self, $type, $args, $opt ) = @_; | 
| 4189 | 59 |  | 50 |  |  | 169 | $args ||= []; | 
| 4190 | 59 |  | 100 |  |  | 285 | $opt ||= {}; | 
| 4191 | 59 |  | 100 |  |  | 113 | my $frames = scalar @{$self->{frame} ||= []}; | 
|  | 59 |  |  |  |  | 306 |  | 
| 4192 | 59 | 100 |  |  |  | 191 | my $prior = $frames ? $self->{frame}[-1] : { | 
| 4193 |  |  |  |  |  |  | condition	=> 1, | 
| 4194 |  |  |  |  |  |  | stdout		=> select(), | 
| 4195 |  |  |  |  |  |  | }; | 
| 4196 |  |  |  |  |  |  | my $condition = exists $opt->{condition} ? | 
| 4197 |  |  |  |  |  |  | $opt->{condition} : | 
| 4198 | 59 | 100 |  |  |  | 163 | $prior->{condition}; | 
| 4199 |  |  |  |  |  |  | ####    defined $stdout or $stdout = select(); | 
| 4200 | 59 |  |  |  |  | 232 | my ( undef, $filename, $line ) = caller; | 
| 4201 | 59 |  |  |  |  | 949 | push @{$self->{frame}}, { | 
| 4202 |  |  |  |  |  |  | type => $type, | 
| 4203 |  |  |  |  |  |  | args => $args, | 
| 4204 |  |  |  |  |  |  | condition	=> $condition, | 
| 4205 |  |  |  |  |  |  | define => {},		# Macro defaults done with := | 
| 4206 |  |  |  |  |  |  | local => {}, | 
| 4207 |  |  |  |  |  |  | localout => undef,	# Output for statement. | 
| 4208 |  |  |  |  |  |  | macro => {}, | 
| 4209 |  |  |  |  |  |  | pushed_by => "$filename line $line", | 
| 4210 |  |  |  |  |  |  | spacetrack => {}, | 
| 4211 |  |  |  |  |  |  | stdout => $prior->{localout} || $prior->{stdout}, | 
| 4212 | 59 |  | 66 |  |  | 119 | unsatisfied_if	=> $prior->{unsatisfied_if} || ! $condition, | 
|  |  |  | 100 |  |  |  |  | 
| 4213 |  |  |  |  |  |  | }; | 
| 4214 | 59 |  |  |  |  | 190 | return $frames; | 
| 4215 |  |  |  |  |  |  | } | 
| 4216 |  |  |  |  |  |  |  | 
| 4217 |  |  |  |  |  |  | #	$satpass2->_frame_pop($frames); | 
| 4218 |  |  |  |  |  |  | #	$satpass2->_frame_pop($type => $frames); | 
| 4219 |  |  |  |  |  |  | #	$satpass2->_frame_pop(); | 
| 4220 |  |  |  |  |  |  | # | 
| 4221 |  |  |  |  |  |  | #	This method pops context frames off the stack until there are | 
| 4222 |  |  |  |  |  |  | #	$frames frames left. The optional $type argument is currently | 
| 4223 |  |  |  |  |  |  | #	unused, but was intended for type checking should that become | 
| 4224 |  |  |  |  |  |  | #	necessary. The zero-argument call pops one frame off the stack. | 
| 4225 |  |  |  |  |  |  | #	An exception is thrown if there are no frames left to pop. After | 
| 4226 |  |  |  |  |  |  | #	all required frames are popped, an exception is thrown if the | 
| 4227 |  |  |  |  |  |  | #	pop was done with a continued input line pending. | 
| 4228 |  |  |  |  |  |  |  | 
| 4229 |  |  |  |  |  |  | { | 
| 4230 |  |  |  |  |  |  |  | 
| 4231 |  |  |  |  |  |  | my %force_set;	# If true, the named attribute is set with the | 
| 4232 |  |  |  |  |  |  | # set() method even if a hash key of the same | 
| 4233 |  |  |  |  |  |  | # name exists. This is set with | 
| 4234 |  |  |  |  |  |  | # _frame_pop_force_set(), typically where the | 
| 4235 |  |  |  |  |  |  | # mutator is defined. | 
| 4236 |  |  |  |  |  |  |  | 
| 4237 |  |  |  |  |  |  | sub _frame_pop { | 
| 4238 | 53 |  |  | 53 |  | 137 | my ($self, @args) = @_; | 
| 4239 |  |  |  |  |  |  | ##	my $type = @args > 1 ? shift @args : undef; | 
| 4240 | 53 | 100 |  |  |  | 140 | @args > 1 and shift @args;	# Currently unused | 
| 4241 |  |  |  |  |  |  | my $frames = ( @args && defined $args[0] ) ? | 
| 4242 |  |  |  |  |  |  | shift @args : | 
| 4243 | 53 | 100 | 100 |  |  | 259 | @{$self->{frame}} - 1; | 
|  | 27 |  |  |  |  | 74 |  | 
| 4244 | 53 |  |  |  |  | 98 | while (@{$self->{frame}} > $frames) { | 
|  | 105 |  |  |  |  | 312 |  | 
| 4245 | 52 | 50 |  |  |  | 88 | my $frame = pop @{$self->{frame}} | 
|  | 52 |  |  |  |  | 173 |  | 
| 4246 |  |  |  |  |  |  | or $self->weep( 'No frame to pop' ); | 
| 4247 | 52 |  | 50 |  |  | 151 | my $local = $frame->{local} || {}; | 
| 4248 | 52 |  |  |  |  | 86 | foreach my $name ( keys %{ $local } ) { | 
|  | 52 |  |  |  |  | 185 |  | 
| 4249 | 2 |  |  |  |  | 15 | my $value = $local->{$name}; | 
| 4250 | 2 | 100 | 66 |  |  | 29 | if ( exists $self->{$name} && !$force_set{$name} ) { | 
| 4251 | 1 |  |  |  |  | 16 | $self->{$name} = $value; | 
| 4252 |  |  |  |  |  |  | } else { | 
| 4253 | 1 |  |  |  |  | 12 | $self->set( $name, $value ); | 
| 4254 |  |  |  |  |  |  | } | 
| 4255 |  |  |  |  |  |  | } | 
| 4256 | 52 |  |  |  |  | 123 | foreach my $key (qw{macro}) { | 
| 4257 | 52 |  | 50 |  |  | 173 | my $info = $frame->{$key} || {}; | 
| 4258 | 52 |  |  |  |  | 84 | foreach my $name ( keys %{ $info } ) { | 
|  | 52 |  |  |  |  | 151 |  | 
| 4259 | 19 |  |  |  |  | 68 | $self->{$key}{$name} = $info->{ $name }; | 
| 4260 |  |  |  |  |  |  | } | 
| 4261 |  |  |  |  |  |  | } | 
| 4262 | 52 |  |  |  |  | 338 | ($frame->{spacetrack} && %{$frame->{spacetrack}}) | 
| 4263 | 52 | 50 | 33 |  |  | 163 | and $self->_get_spacetrack()->set(%{$frame->{spacetrack}}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 4264 |  |  |  |  |  |  | } | 
| 4265 | 53 | 50 |  |  |  | 155 | if (delete $self->{pending}) { | 
| 4266 | 0 |  |  |  |  | 0 | $self->wail('Input ended on continued line'); | 
| 4267 |  |  |  |  |  |  | } | 
| 4268 | 53 |  |  |  |  | 123 | return; | 
| 4269 |  |  |  |  |  |  | } | 
| 4270 |  |  |  |  |  |  |  | 
| 4271 |  |  |  |  |  |  | # Force use of the set() method even if there is an attribute of the | 
| 4272 |  |  |  |  |  |  | # same name. | 
| 4273 |  |  |  |  |  |  | sub _frame_pop_force_set { | 
| 4274 | 20 |  |  | 20 |  | 68 | foreach my $name ( @_ ) { | 
| 4275 | 20 |  |  |  |  | 71 | $force_set{$name} = 1; | 
| 4276 |  |  |  |  |  |  | } | 
| 4277 | 20 |  |  |  |  | 42 | return; | 
| 4278 |  |  |  |  |  |  | } | 
| 4279 |  |  |  |  |  |  | } | 
| 4280 |  |  |  |  |  |  |  | 
| 4281 |  |  |  |  |  |  | sub _get_browser_command { | 
| 4282 | 0 |  |  | 0 |  | 0 | my ( $self, $val ) = @_; | 
| 4283 |  |  |  |  |  |  | defined $val | 
| 4284 | 0 | 0 |  |  |  | 0 | or $val = $self->{webcmd}; | 
| 4285 | 0 | 0 | 0 |  |  | 0 | defined $val | 
| 4286 |  |  |  |  |  |  | and '' ne $val | 
| 4287 |  |  |  |  |  |  | or return $val; | 
| 4288 | 0 | 0 |  |  |  | 0 | '1' eq $val | 
| 4289 |  |  |  |  |  |  | or return $val; | 
| 4290 | 0 |  |  |  |  | 0 | require Browser::Open; | 
| 4291 | 0 |  |  |  |  | 0 | return Browser::Open::open_browser_cmd(); | 
| 4292 |  |  |  |  |  |  | } | 
| 4293 |  |  |  |  |  |  |  | 
| 4294 |  |  |  |  |  |  | #	$dumper = $self->_get_dumper(); | 
| 4295 |  |  |  |  |  |  | # | 
| 4296 |  |  |  |  |  |  | #	This method returns a reference to code that can be used to dump | 
| 4297 |  |  |  |  |  |  | #	data. The first time it is called it goes through a list of | 
| 4298 |  |  |  |  |  |  | #	possible classes, and uses the first one it can load, dying if | 
| 4299 |  |  |  |  |  |  | #	it can not load any of them. After the first successful call, it | 
| 4300 |  |  |  |  |  |  | #	simply returns the cached dumper. | 
| 4301 |  |  |  |  |  |  |  | 
| 4302 |  |  |  |  |  |  | { | 
| 4303 |  |  |  |  |  |  | my $dumper; | 
| 4304 |  |  |  |  |  |  | my %kode = ( | 
| 4305 |  |  |  |  |  |  | 'Data::Dumper' => sub { | 
| 4306 |  |  |  |  |  |  | local $Data::Dumper::Terse = 1; | 
| 4307 |  |  |  |  |  |  | Data::Dumper::Dumper(@_); | 
| 4308 |  |  |  |  |  |  | }, | 
| 4309 |  |  |  |  |  |  | ); | 
| 4310 |  |  |  |  |  |  | sub _get_dumper { | 
| 4311 | 0 |  |  | 0 |  | 0 | my ($self) = @_; | 
| 4312 | 0 |  |  |  |  | 0 | my %dmpr; | 
| 4313 |  |  |  |  |  |  | my @mod; | 
| 4314 | 0 |  | 0 |  |  | 0 | return $dumper ||= do { | 
| 4315 | 0 |  |  |  |  | 0 | foreach (qw{YAML::Dump Data::Dumper::Dumper}) { | 
| 4316 | 0 |  |  |  |  | 0 | my ($module, $routine) = m/ (.*) :: (.*) /smx; | 
| 4317 | 0 |  |  |  |  | 0 | push @mod, $module; | 
| 4318 | 0 |  |  |  |  | 0 | $dmpr{$module} = $routine; | 
| 4319 |  |  |  |  |  |  | } | 
| 4320 | 0 |  |  |  |  | 0 | my $mod = $self->_load_module(@mod); | 
| 4321 | 0 | 0 |  |  |  | 0 | $kode{$mod} || $mod->can($dmpr{$mod}); | 
| 4322 |  |  |  |  |  |  | }; | 
| 4323 |  |  |  |  |  |  | } | 
| 4324 |  |  |  |  |  |  | } | 
| 4325 |  |  |  |  |  |  |  | 
| 4326 |  |  |  |  |  |  | #	$fmt = $satpass2->_get_dumper_object(); | 
| 4327 |  |  |  |  |  |  | # | 
| 4328 |  |  |  |  |  |  | #	Gets a dumper object. This object must conform to the | 
| 4329 |  |  |  |  |  |  | #	Astro::App::Satpass2::Format interface. | 
| 4330 |  |  |  |  |  |  |  | 
| 4331 |  |  |  |  |  |  | { | 
| 4332 |  |  |  |  |  |  |  | 
| 4333 |  |  |  |  |  |  | my $dumper; | 
| 4334 |  |  |  |  |  |  |  | 
| 4335 |  |  |  |  |  |  | sub _get_dumper_object { | 
| 4336 | 0 |  | 0 | 0 |  | 0 | return ( $dumper ||= do { | 
| 4337 | 0 |  |  |  |  | 0 | require Astro::App::Satpass2::Format::Dump; | 
| 4338 | 0 |  |  |  |  | 0 | Astro::App::Satpass2::Format::Dump->new(); | 
| 4339 |  |  |  |  |  |  | } | 
| 4340 |  |  |  |  |  |  | ); | 
| 4341 |  |  |  |  |  |  | } | 
| 4342 |  |  |  |  |  |  |  | 
| 4343 |  |  |  |  |  |  | } | 
| 4344 |  |  |  |  |  |  |  | 
| 4345 |  |  |  |  |  |  | #	$fmt = $satpass2->_get_formatter_object( $opt ); | 
| 4346 |  |  |  |  |  |  | # | 
| 4347 |  |  |  |  |  |  | #	Gets the Astro::App::Satpass2::Format object. If $opt->{dump} is true, | 
| 4348 |  |  |  |  |  |  | #	returns a dumper object; otherwise returns the currently-set | 
| 4349 |  |  |  |  |  |  | #	formatter object. | 
| 4350 |  |  |  |  |  |  |  | 
| 4351 |  |  |  |  |  |  | sub _get_formatter_object { | 
| 4352 | 41 |  |  | 41 |  | 127 | my ( $self, $opt ) = @_; | 
| 4353 | 41 |  | 50 |  |  | 210 | $opt ||= {}; | 
| 4354 | 41 | 50 | 33 |  |  | 406 | return ( $opt && $opt->{dump} ) ? $self->_get_dumper_object() : | 
| 4355 |  |  |  |  |  |  | $self->get( 'formatter' ); | 
| 4356 |  |  |  |  |  |  | } | 
| 4357 |  |  |  |  |  |  |  | 
| 4358 |  |  |  |  |  |  | sub _get_formatter_attribute { | 
| 4359 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 4360 | 0 |  |  |  |  | 0 | return $self->get( 'formatter' )->$name(); | 
| 4361 |  |  |  |  |  |  | } | 
| 4362 |  |  |  |  |  |  |  | 
| 4363 |  |  |  |  |  |  | #	$st = $satpass2->_get_geocoder() | 
| 4364 |  |  |  |  |  |  |  | 
| 4365 |  |  |  |  |  |  | #	Gets the geocoder object, instantiating it if | 
| 4366 |  |  |  |  |  |  | #	necesary. | 
| 4367 |  |  |  |  |  |  |  | 
| 4368 |  |  |  |  |  |  | sub _get_geocoder { | 
| 4369 | 0 |  |  | 0 |  | 0 | my ( $self ) = @_; | 
| 4370 | 0 | 0 |  |  |  | 0 | if ( ! exists $self->{geocoder} ) { | 
| 4371 | 0 |  |  |  |  | 0 | my ( $class, $obj ); | 
| 4372 | 0 | 0 |  |  |  | 0 | $class = $default_geocoder->() | 
| 4373 |  |  |  |  |  |  | and $obj = $class->new(); | 
| 4374 | 0 |  |  |  |  | 0 | $self->{geocoder} = $obj; | 
| 4375 |  |  |  |  |  |  | } | 
| 4376 | 0 |  |  |  |  | 0 | return $self->{geocoder}; | 
| 4377 |  |  |  |  |  |  | } | 
| 4378 |  |  |  |  |  |  |  | 
| 4379 |  |  |  |  |  |  | #	$boolean = $satpass2->_get_interactive(); | 
| 4380 |  |  |  |  |  |  | # | 
| 4381 |  |  |  |  |  |  | #	This method returns true if the script is running interactively, | 
| 4382 |  |  |  |  |  |  | #	and false otherwise. Currently, it returns the results of -t | 
| 4383 |  |  |  |  |  |  | #	STDIN. | 
| 4384 |  |  |  |  |  |  |  | 
| 4385 |  |  |  |  |  |  | sub _get_interactive { | 
| 4386 | 1 |  |  | 1 |  | 12 | return -t STDIN; | 
| 4387 |  |  |  |  |  |  | } | 
| 4388 |  |  |  |  |  |  |  | 
| 4389 |  |  |  |  |  |  | #	$code = $satpass2->_get_readline(); | 
| 4390 |  |  |  |  |  |  | # | 
| 4391 |  |  |  |  |  |  | #	Returns code to read input. The code takes an argument which | 
| 4392 |  |  |  |  |  |  | #	will be used as a prompt if one is needed. What is actually | 
| 4393 |  |  |  |  |  |  | #	returned is: | 
| 4394 |  |  |  |  |  |  | # | 
| 4395 |  |  |  |  |  |  | #	If $satpass2->_get_interactive() is false, the returned code | 
| 4396 |  |  |  |  |  |  | #	just reads standard in. Otherwise, | 
| 4397 |  |  |  |  |  |  | # | 
| 4398 |  |  |  |  |  |  | #	if Term::ReadLine can be loaded, a Term::ReadLine object is | 
| 4399 |  |  |  |  |  |  | #	instantiated if need be, and the returned code calls | 
| 4400 |  |  |  |  |  |  | #	Term::ReadLine->readline($_[0]) and returns whatever that gives | 
| 4401 |  |  |  |  |  |  | #	you. Otherwise, | 
| 4402 |  |  |  |  |  |  | # | 
| 4403 |  |  |  |  |  |  | #	Otherwise the returned code writes its argument to STDERR and | 
| 4404 |  |  |  |  |  |  | #	reads STDIN. | 
| 4405 |  |  |  |  |  |  | # | 
| 4406 |  |  |  |  |  |  | #	Note that the return from this subroutine may or may not be | 
| 4407 |  |  |  |  |  |  | #	chomped. | 
| 4408 |  |  |  |  |  |  |  | 
| 4409 |  |  |  |  |  |  | my $readline_word_break_re; | 
| 4410 |  |  |  |  |  |  |  | 
| 4411 |  |  |  |  |  |  | { | 
| 4412 |  |  |  |  |  |  | my $rl; | 
| 4413 |  |  |  |  |  |  |  | 
| 4414 |  |  |  |  |  |  | sub _get_readline { | 
| 4415 | 1 |  |  | 1 |  | 7 | my ($self) = @_; | 
| 4416 |  |  |  |  |  |  | # The Perl::Critic recommendation is IO::Interactive, but that | 
| 4417 |  |  |  |  |  |  | # fiddles with STDOUT. We want STDIN, because we want to behave | 
| 4418 |  |  |  |  |  |  | # differently if STDIN is a pipe, but not if STDOUT is a pipe. | 
| 4419 |  |  |  |  |  |  | # We're still missing the *ARGV logic, but that's OK too, since | 
| 4420 |  |  |  |  |  |  | # we use the contents of @ARGV as commands, not as file names. | 
| 4421 | 1 |  |  |  |  | 2 | return do { | 
| 4422 | 1 |  |  |  |  | 3 | my $buffer = ''; | 
| 4423 | 1 | 50 |  |  |  | 4 | if ($self->_get_interactive()) { | 
| 4424 |  |  |  |  |  |  | eval { | 
| 4425 | 0 | 0 |  |  |  | 0 | load_package( 'Term::ReadLine' ) | 
| 4426 |  |  |  |  |  |  | or return; | 
| 4427 | 0 | 0 |  |  |  | 0 | unless ( $rl ) { | 
| 4428 | 0 |  |  |  |  | 0 | $rl = Term::ReadLine->new( 'satpass2' ); | 
| 4429 | 0 | 0 |  |  |  | 0 | if ( 'Term::ReadLine::Perl' eq $rl->ReadLine() ) { | 
| 4430 |  |  |  |  |  |  |  | 
| 4431 | 0 |  | 0 |  |  | 0 | $readline_word_break_re ||= qr< | 
| 4432 |  |  |  |  |  |  | [\Q$readline::rl_completer_word_break_characters\E]+ | 
| 4433 |  |  |  |  |  |  | >smx; | 
| 4434 |  |  |  |  |  |  |  | 
| 4435 | 20 |  |  | 20 |  | 106380 | no warnings qw{ once }; | 
|  | 20 |  |  |  |  | 53 |  | 
|  | 20 |  |  |  |  | 16148 |  | 
| 4436 |  |  |  |  |  |  | $readline::rl_completion_function = sub { | 
| 4437 | 0 |  |  | 0 |  | 0 | my ( $text, $line, $start ) = @_; | 
| 4438 | 0 |  |  |  |  | 0 | return $self->__readline_completer( | 
| 4439 |  |  |  |  |  |  | $text, $line, $start ); | 
| 4440 | 0 |  |  |  |  | 0 | }; | 
| 4441 |  |  |  |  |  |  | } | 
| 4442 |  |  |  |  |  |  | } | 
| 4443 |  |  |  |  |  |  | sub { | 
| 4444 | 0 | 0 |  | 0 |  | 0 | defined $buffer or return $buffer; | 
| 4445 | 0 |  |  |  |  | 0 | return ( $buffer = $rl->readline($_[0]) ); | 
| 4446 |  |  |  |  |  |  | } | 
| 4447 | 0 |  |  |  |  | 0 | } || sub { | 
| 4448 | 0 | 0 |  | 0 |  | 0 | defined $buffer or return $buffer; | 
| 4449 | 0 |  |  |  |  | 0 | print STDERR $_[0]; | 
| 4450 |  |  |  |  |  |  | return ( | 
| 4451 | 0 |  |  |  |  | 0 | $buffer = 	## no critic (ProhibitExplicitStdin) | 
| 4452 |  |  |  |  |  |  | ); | 
| 4453 | 0 | 0 |  |  |  | 0 | }; | 
| 4454 |  |  |  |  |  |  | } else { | 
| 4455 |  |  |  |  |  |  | sub { | 
| 4456 | 0 | 0 |  | 0 |  | 0 | defined $buffer or return $buffer; | 
| 4457 |  |  |  |  |  |  | return ( | 
| 4458 | 0 |  |  |  |  | 0 | $buffer = 	## no critic (ProhibitExplicitStdin) | 
| 4459 |  |  |  |  |  |  | ); | 
| 4460 | 1 |  |  |  |  | 8 | }; | 
| 4461 |  |  |  |  |  |  | } | 
| 4462 |  |  |  |  |  |  | }; | 
| 4463 |  |  |  |  |  |  | } | 
| 4464 |  |  |  |  |  |  | } | 
| 4465 |  |  |  |  |  |  |  | 
| 4466 |  |  |  |  |  |  | sub __readline_completer { | 
| 4467 | 0 |  |  | 0 |  | 0 | my ( $app, $text, $line, $start ) = @_; | 
| 4468 |  |  |  |  |  |  |  | 
| 4469 | 0 | 0 |  |  |  | 0 | $start | 
| 4470 |  |  |  |  |  |  | or return $app->_readline_complete_command( $text ); | 
| 4471 |  |  |  |  |  |  |  | 
| 4472 | 0 |  |  |  |  | 0 | my ( $cmd ) = split $readline_word_break_re, $line, 2; | 
| 4473 | 0 |  |  |  |  | 0 | my $code; | 
| 4474 |  |  |  |  |  |  | not $cmd =~ s/ \A core [.] //smx | 
| 4475 |  |  |  |  |  |  | and ref $app | 
| 4476 |  |  |  |  |  |  | and $app->{macro}{$cmd} | 
| 4477 | 0 | 0 | 0 |  |  | 0 | and $code = $app->{macro}{$cmd}->implements( $cmd ); | 
|  |  |  | 0 |  |  |  |  | 
| 4478 | 0 |  | 0 |  |  | 0 | $code ||= $app->can( $cmd ); | 
| 4479 |  |  |  |  |  |  |  | 
| 4480 | 0 | 0 |  |  |  | 0 | if ( CODE_REF eq ref $code ) { | 
|  |  | 0 |  |  |  |  |  | 
| 4481 |  |  |  |  |  |  | # builtins and code macros go here | 
| 4482 |  |  |  |  |  |  |  | 
| 4483 | 0 |  |  |  |  | 0 | my $rslt; | 
| 4484 |  |  |  |  |  |  |  | 
| 4485 | 0 | 0 |  |  |  | 0 | if ( my $method = $app->__get_attr( $code, Tweak => {} | 
| 4486 |  |  |  |  |  |  | )->{completion} ) { | 
| 4487 |  |  |  |  |  |  | $rslt = $app->$method( $code, $text, $line, $start ) | 
| 4488 | 0 | 0 |  |  |  | 0 | and return @{ $rslt }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4489 |  |  |  |  |  |  | } | 
| 4490 |  |  |  |  |  |  |  | 
| 4491 |  |  |  |  |  |  | $rslt = $app->_readline_complete_options( $code, $text, | 
| 4492 |  |  |  |  |  |  | $line, $start ) | 
| 4493 | 0 |  |  |  |  | 0 | and @{ $rslt } | 
| 4494 | 0 | 0 | 0 |  |  | 0 | and return @{ $rslt }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4495 |  |  |  |  |  |  |  | 
| 4496 |  |  |  |  |  |  | } elsif ( my $macro = $app->{macro}{$cmd} ) { | 
| 4497 |  |  |  |  |  |  | # command macros go here | 
| 4498 |  |  |  |  |  |  |  | 
| 4499 | 0 |  |  |  |  | 0 | my $rslt; | 
| 4500 |  |  |  |  |  |  | $rslt = $macro->completion( $text ) | 
| 4501 | 0 | 0 |  |  |  | 0 | and return @{ $rslt }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4502 |  |  |  |  |  |  | } | 
| 4503 |  |  |  |  |  |  |  | 
| 4504 | 0 |  |  |  |  | 0 | my @files = bsd_glob( "$text*" ); | 
| 4505 | 0 | 0 |  |  |  | 0 | if ( 1 == @files ) { | 
|  |  | 0 |  |  |  |  |  | 
| 4506 | 0 | 0 |  |  |  | 0 | $files[0] .= -d $files[0] ? '/' : ' '; | 
| 4507 |  |  |  |  |  |  | } elsif ( $readline::var_CompleteAddsuffix ) { | 
| 4508 | 0 |  |  |  |  | 0 | foreach ( @files ) { | 
| 4509 | 0 | 0 | 0 |  |  | 0 | if ( -l $_ ) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 4510 | 0 |  |  |  |  | 0 | $_ .= '@'; | 
| 4511 |  |  |  |  |  |  | } elsif ( -d $_ ) { | 
| 4512 | 0 |  |  |  |  | 0 | $_ .= '/'; | 
| 4513 |  |  |  |  |  |  | } elsif ( -x _) { | 
| 4514 | 0 |  |  |  |  | 0 | $_ .= '*'; | 
| 4515 |  |  |  |  |  |  | } elsif ( -S _ || -p _ ) { | 
| 4516 | 0 |  |  |  |  | 0 | $_ .= '='; | 
| 4517 |  |  |  |  |  |  | } | 
| 4518 |  |  |  |  |  |  | } | 
| 4519 |  |  |  |  |  |  | } | 
| 4520 | 0 |  |  |  |  | 0 | $readline::rl_completer_terminator_character = ''; | 
| 4521 | 0 |  |  |  |  | 0 | return @files; | 
| 4522 |  |  |  |  |  |  | } | 
| 4523 |  |  |  |  |  |  |  | 
| 4524 |  |  |  |  |  |  | { | 
| 4525 |  |  |  |  |  |  | my @builtins; | 
| 4526 |  |  |  |  |  |  | sub _readline_complete_command { | 
| 4527 | 0 |  |  | 0 |  | 0 | my ( $app, $text ) = @_; | 
| 4528 | 0 | 0 |  |  |  | 0 | unless ( @builtins ) { | 
| 4529 | 0 |  | 0 |  |  | 0 | my $stash = ( ref $app || $app ) . '::'; | 
| 4530 | 20 |  |  | 20 |  | 182 | no strict qw{ refs }; | 
|  | 20 |  |  |  |  | 62 |  | 
|  | 20 |  |  |  |  | 15917 |  | 
| 4531 | 0 |  |  |  |  | 0 | foreach my $sym ( keys %$stash ) { | 
| 4532 | 0 | 0 |  |  |  | 0 | $sym =~ m/ \A _ /smx | 
| 4533 |  |  |  |  |  |  | and next; | 
| 4534 | 0 | 0 |  |  |  | 0 | my $code = $app->can( $sym ) | 
| 4535 |  |  |  |  |  |  | or next; | 
| 4536 | 0 | 0 |  |  |  | 0 | $app->__get_attr( $code, 'Verb' ) | 
| 4537 |  |  |  |  |  |  | or next; | 
| 4538 | 0 |  |  |  |  | 0 | push @builtins, $sym; | 
| 4539 |  |  |  |  |  |  | } | 
| 4540 | 0 |  |  |  |  | 0 | @builtins = sort @builtins; | 
| 4541 |  |  |  |  |  |  | } | 
| 4542 | 0 |  |  |  |  | 0 | my @rslt; | 
| 4543 | 0 | 0 |  |  |  | 0 | if ( $text =~ s/ \A core [.] //smx ) { | 
| 4544 | 0 |  |  |  |  | 0 | my $match = qr< \A \Q$text\E >smx; | 
| 4545 | 0 |  |  |  |  | 0 | @rslt = map { "core.$_" } grep { $_ =~ $match } @builtins; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 4546 |  |  |  |  |  |  | } else { | 
| 4547 | 0 |  |  |  |  | 0 | my $match = qr< \A \Q$text\E >smx; | 
| 4548 | 0 |  |  |  |  | 0 | @rslt = grep { $_ =~ $match } @builtins, 'core.', | 
| 4549 | 0 | 0 |  |  |  | 0 | ref $app ? keys %{ $app->{macro} } : (); | 
|  | 0 |  |  |  |  | 0 |  | 
| 4550 |  |  |  |  |  |  | } | 
| 4551 | 0 | 0 | 0 |  |  | 0 | 1 == @rslt | 
| 4552 |  |  |  |  |  |  | and $rslt[0] =~ m/ \W \z /smx | 
| 4553 |  |  |  |  |  |  | and $readline::rl_completer_terminator_character = ''; | 
| 4554 | 0 |  |  |  |  | 0 | return ( sort @rslt ); | 
| 4555 |  |  |  |  |  |  | } | 
| 4556 |  |  |  |  |  |  | } | 
| 4557 |  |  |  |  |  |  |  | 
| 4558 |  |  |  |  |  |  | sub _readline_complete_options { | 
| 4559 |  |  |  |  |  |  | # my ( $app, $code, $text, $line, $start ) = @_; | 
| 4560 | 0 |  |  | 0 |  | 0 | my ( $app, $code, $text ) = @_; | 
| 4561 | 0 | 0 |  |  |  | 0 | $text =~ m/ \A ( --? ) ( .* ) /smx | 
| 4562 |  |  |  |  |  |  | or return; | 
| 4563 | 0 |  |  |  |  | 0 | my ( $prefix, $match ) = ( $1, $2 ); | 
| 4564 | 0 |  |  |  |  | 0 | my $lgl = $app->__legal_options( $code ); | 
| 4565 | 0 |  |  |  |  | 0 | my $re = qr< \A \Q$match\E >smx; | 
| 4566 | 0 |  |  |  |  | 0 | my @rslt; | 
| 4567 | 0 |  |  |  |  | 0 | foreach ( @{ $lgl } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4568 | 0 | 0 |  |  |  | 0 | next if ref; | 
| 4569 |  |  |  |  |  |  | # De-alias before modifying | 
| 4570 | 0 |  |  |  |  | 0 | ( my $o = $_ ) =~ s/ [!=?] .* //smx; | 
| 4571 | 0 |  |  |  |  | 0 | push @rslt, grep { m/$re/ } split qr< \| >smx, $o; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4572 |  |  |  |  |  |  | } | 
| 4573 |  |  |  |  |  |  | @rslt | 
| 4574 | 0 | 0 |  |  |  | 0 | and return [ map { "$prefix$_" } sort @rslt ]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4575 | 0 |  |  |  |  | 0 | return; | 
| 4576 |  |  |  |  |  |  | } | 
| 4577 |  |  |  |  |  |  |  | 
| 4578 |  |  |  |  |  |  | # The following subroutine is called dynamically | 
| 4579 |  |  |  |  |  |  | sub _readline_complete_subcommand { ## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4580 |  |  |  |  |  |  | # my ( $app, $code, $text, $line, $start ) = @_; | 
| 4581 | 0 |  |  | 0 |  | 0 | my ( $app, undef, $text, $line, $start ) = @_; | 
| 4582 | 0 |  |  |  |  | 0 | my @part = _readline_line_to_parts( $line ); | 
| 4583 | 0 | 0 |  |  |  | 0 | if ( my $code = $app->can( "_$part[0]_sub" ) ) { | 
| 4584 | 0 |  |  |  |  | 0 | return $code->( $app, $text, $line, $start, @part ); | 
| 4585 |  |  |  |  |  |  | } | 
| 4586 | 0 |  |  |  |  | 0 | my @rslt; | 
| 4587 | 0 | 0 |  |  |  | 0 | if ( 2 == @part ) { | 
| 4588 | 0 |  |  |  |  | 0 | my $re = qr< \A _$part[0]_sub_ ( \Q$part[1]\E \w* ) >smx; | 
| 4589 | 0 |  | 0 |  |  | 0 | my $stash = ( ref $app || $app ) . '::'; | 
| 4590 | 20 |  |  | 20 |  | 196 | no strict qw{ refs }; | 
|  | 20 |  |  |  |  | 47 |  | 
|  | 20 |  |  |  |  | 34570 |  | 
| 4591 | 0 |  |  |  |  | 0 | foreach my $key ( keys %$stash ) { | 
| 4592 | 0 | 0 |  |  |  | 0 | $key =~ m/$re/smx | 
| 4593 |  |  |  |  |  |  | and push @rslt, "$1"; | 
| 4594 |  |  |  |  |  |  | } | 
| 4595 | 0 |  |  |  |  | 0 | return [ sort @rslt ]; | 
| 4596 |  |  |  |  |  |  | } | 
| 4597 |  |  |  |  |  |  |  | 
| 4598 | 0 | 0 |  |  |  | 0 | my $code = $app->can( "_$part[0]_sub_$part[1]" ) | 
| 4599 |  |  |  |  |  |  | or return; | 
| 4600 |  |  |  |  |  |  |  | 
| 4601 | 0 |  |  |  |  | 0 | my $r; | 
| 4602 | 0 | 0 |  |  |  | 0 | $r = $app->_readline_complete_options( $code, $text, $line, | 
| 4603 |  |  |  |  |  |  | $start ) | 
| 4604 |  |  |  |  |  |  | and return $r; | 
| 4605 |  |  |  |  |  |  |  | 
| 4606 |  |  |  |  |  |  | my $complete = $app->__get_attr( $code, Tweak => {} )->{completion} | 
| 4607 | 0 | 0 |  |  |  | 0 | or return; | 
| 4608 |  |  |  |  |  |  |  | 
| 4609 | 0 | 0 |  |  |  | 0 | $r = $app->$complete( $code, $text, $line, $start ) | 
| 4610 |  |  |  |  |  |  | and return $r; | 
| 4611 |  |  |  |  |  |  |  | 
| 4612 | 0 |  |  |  |  | 0 | return; | 
| 4613 |  |  |  |  |  |  | } | 
| 4614 |  |  |  |  |  |  |  | 
| 4615 |  |  |  |  |  |  | sub _macro_list_complete {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4616 |  |  |  |  |  |  | # my ( $app, $code, $text, $line, $start ) = @_; | 
| 4617 | 0 |  |  | 0 |  | 0 | my ( $app, undef, undef, $line, undef ) = @_; | 
| 4618 | 0 | 0 |  |  |  | 0 | ref $app | 
| 4619 |  |  |  |  |  |  | or return; | 
| 4620 | 0 |  |  |  |  | 0 | my @part = _readline_line_to_parts( $line ); | 
| 4621 | 0 | 0 |  |  |  | 0 | 3 == @part | 
| 4622 |  |  |  |  |  |  | or return; | 
| 4623 | 0 |  |  |  |  | 0 | my $re = qr< \A \Q$part[2]\E >smx; | 
| 4624 | 0 |  |  |  |  | 0 | my @rslt; | 
| 4625 | 0 |  |  |  |  | 0 | foreach ( sort keys %{ $app->{macro} } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4626 | 0 | 0 |  |  |  | 0 | m/$re/smx | 
| 4627 |  |  |  |  |  |  | and push @rslt, $_; | 
| 4628 |  |  |  |  |  |  | } | 
| 4629 | 0 |  |  |  |  | 0 | return \@rslt; | 
| 4630 |  |  |  |  |  |  | } | 
| 4631 |  |  |  |  |  |  |  | 
| 4632 |  |  |  |  |  |  | sub _sky_body_complete {	## no critic (ProhibitUnusedPrivateSubroutines) | 
| 4633 |  |  |  |  |  |  | # my ( $app, $code, $text, $line, $start ) = @_; | 
| 4634 | 0 |  |  | 0 |  | 0 | my ( $app, undef, undef, $line, undef ) = @_; | 
| 4635 | 0 | 0 |  |  |  | 0 | ref $app | 
| 4636 |  |  |  |  |  |  | or return; | 
| 4637 | 0 |  |  |  |  | 0 | my @part = _readline_line_to_parts( $line ); | 
| 4638 | 0 | 0 |  |  |  | 0 | 3 == @part | 
| 4639 |  |  |  |  |  |  | or return; | 
| 4640 | 0 |  |  |  |  | 0 | my $re = qr< \A \Q$part[2]\E >smxi; | 
| 4641 | 0 |  |  |  |  | 0 | my @rslt; | 
| 4642 | 0 |  |  |  |  | 0 | foreach my $body ( @{ $app->{sky} } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4643 | 0 | 0 |  |  |  | 0 | if ( ( my $name = $body->get( 'name' ) ) =~ $re ) { | 
|  |  | 0 |  |  |  |  |  | 
| 4644 | 0 |  |  |  |  | 0 | push @rslt, $name; | 
| 4645 |  |  |  |  |  |  | } elsif ( ( my $id = $body->get( 'id' ) ) =~ $re ) { | 
| 4646 | 0 |  |  |  |  | 0 | push @rslt, $id; | 
| 4647 |  |  |  |  |  |  | } | 
| 4648 |  |  |  |  |  |  | } | 
| 4649 | 0 |  |  |  |  | 0 | return [ sort @rslt ]; | 
| 4650 |  |  |  |  |  |  | } | 
| 4651 |  |  |  |  |  |  |  | 
| 4652 |  |  |  |  |  |  | sub _readline_line_to_parts { | 
| 4653 | 0 |  |  | 0 |  | 0 | my ( $line ) = @_; | 
| 4654 |  |  |  |  |  |  | # NOTE that the field count of -1 causes a trailing separator to | 
| 4655 |  |  |  |  |  |  | # result in a trailing empty field. | 
| 4656 | 0 |  |  |  |  | 0 | my @parts = split $readline_word_break_re, $line, -1; | 
| 4657 |  |  |  |  |  |  | # NOTE that we strip the leading 'core.' if any, so the return from | 
| 4658 |  |  |  |  |  |  | # this method does not distinguish between a core command and the | 
| 4659 |  |  |  |  |  |  | # same-named macro if any. | 
| 4660 |  |  |  |  |  |  | @parts | 
| 4661 | 0 | 0 |  |  |  | 0 | and $parts[0] =~ s/ \A core [.] //smx; | 
| 4662 | 0 |  |  |  |  | 0 | return @parts; | 
| 4663 |  |  |  |  |  |  | } | 
| 4664 |  |  |  |  |  |  |  | 
| 4665 |  |  |  |  |  |  | sub _get_time_parser_attribute { | 
| 4666 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 4667 | 0 |  |  |  |  | 0 | return $self->{time_parser}->$name(); | 
| 4668 |  |  |  |  |  |  | } | 
| 4669 |  |  |  |  |  |  |  | 
| 4670 |  |  |  |  |  |  | #	$st = $satpass2->_get_spacetrack() | 
| 4671 |  |  |  |  |  |  |  | 
| 4672 |  |  |  |  |  |  | #	Gets the Astro::SpaceTrack object, instantiating it if | 
| 4673 |  |  |  |  |  |  | #	necesary. | 
| 4674 |  |  |  |  |  |  |  | 
| 4675 |  |  |  |  |  |  | sub _get_spacetrack { | 
| 4676 | 7 |  |  | 7 |  | 25 | my ( $self ) = @_; | 
| 4677 |  |  |  |  |  |  | exists $self->{spacetrack} | 
| 4678 | 7 | 50 |  |  |  | 60 | or $self->{spacetrack} = $self->_get_spacetrack_default(); | 
| 4679 | 7 |  |  |  |  | 37 | return $self->{spacetrack}; | 
| 4680 |  |  |  |  |  |  | } | 
| 4681 |  |  |  |  |  |  |  | 
| 4682 |  |  |  |  |  |  | #	$st = $satpass2->_get_spacetrack_default(); | 
| 4683 |  |  |  |  |  |  | # | 
| 4684 |  |  |  |  |  |  | #	Returns a new Astro::SpaceTrack object, initialized with this | 
| 4685 |  |  |  |  |  |  | #	object's webcmd, and with its filter attribute set to 1 and its | 
| 4686 |  |  |  |  |  |  | #	iridium_status_format set to 'kelso'. | 
| 4687 |  |  |  |  |  |  |  | 
| 4688 |  |  |  |  |  |  | sub _get_spacetrack_default { | 
| 4689 | 7 |  |  | 7 |  | 35 | my ( $self ) = @_; | 
| 4690 | 7 | 50 |  |  |  | 33 | $have_astro_spacetrack->() | 
| 4691 |  |  |  |  |  |  | or return; | 
| 4692 |  |  |  |  |  |  | return Astro::SpaceTrack->new ( | 
| 4693 |  |  |  |  |  |  | webcmd => $self->{webcmd}, | 
| 4694 | 0 |  |  |  |  | 0 | filter => 1, | 
| 4695 |  |  |  |  |  |  | iridium_status_format => 'kelso', | 
| 4696 |  |  |  |  |  |  | ); | 
| 4697 |  |  |  |  |  |  | } | 
| 4698 |  |  |  |  |  |  |  | 
| 4699 |  |  |  |  |  |  | sub _get_day_midnight { | 
| 4700 | 10 |  |  | 10 |  | 62 | my ( $self, $day ) = @_; | 
| 4701 | 10 | 100 |  |  |  | 55 | defined $day | 
| 4702 |  |  |  |  |  |  | or $day = time; | 
| 4703 | 10 |  |  |  |  | 45 | my $gmt = $self->get( 'formatter' )->gmt(); | 
| 4704 | 10 | 50 |  |  |  | 101 | my @time = $gmt ? gmtime( $day ) : localtime( $day ); | 
| 4705 | 10 |  |  |  |  | 50 | $time[0] = $time[1] = $time[2] = 0; | 
| 4706 | 10 |  |  |  |  | 31 | $time[5] += 1900; | 
| 4707 | 10 | 50 |  |  |  | 69 | return $gmt ? greg_time_gm(@time) : greg_time_local(@time); | 
| 4708 |  |  |  |  |  |  | } | 
| 4709 |  |  |  |  |  |  |  | 
| 4710 |  |  |  |  |  |  | sub _get_day_noon { | 
| 4711 | 42 |  |  | 42 |  | 146 | my ( $self, $day ) = @_; | 
| 4712 | 42 | 100 |  |  |  | 149 | defined $day | 
| 4713 |  |  |  |  |  |  | or $day = time; | 
| 4714 | 42 |  |  |  |  | 170 | my $gmt = $self->get( 'formatter' )->gmt(); | 
| 4715 | 42 | 50 |  |  |  | 305 | my @time = $gmt ? gmtime( $day ) : localtime( $day ); | 
| 4716 | 42 |  |  |  |  | 147 | $time[0] = $time[1] = 0; | 
| 4717 | 42 |  |  |  |  | 103 | $time[2] = 12; | 
| 4718 | 42 |  |  |  |  | 156 | $time[5] += 1900; | 
| 4719 | 42 | 50 |  |  |  | 267 | return $gmt ? greg_time_gm(@time) : greg_time_local(@time); | 
| 4720 |  |  |  |  |  |  | } | 
| 4721 |  |  |  |  |  |  |  | 
| 4722 |  |  |  |  |  |  | sub _get_warner_attribute { | 
| 4723 | 0 |  |  | 0 |  | 0 | my ( $self, $name ) = @_; | 
| 4724 | 0 |  |  |  |  | 0 | return $self->{_warner}->$name(); | 
| 4725 |  |  |  |  |  |  | } | 
| 4726 |  |  |  |  |  |  |  | 
| 4727 |  |  |  |  |  |  | sub _helper_get_object { | 
| 4728 | 9 |  |  | 9 |  | 25 | my ( $self, $attribute ) = @_; | 
| 4729 | 9 | 50 |  |  |  | 23 | my $object = $self->get( $attribute ) | 
| 4730 |  |  |  |  |  |  | or $self->wail( "No $attribute object available" ); | 
| 4731 | 9 |  |  |  |  | 24 | return $object; | 
| 4732 |  |  |  |  |  |  | } | 
| 4733 |  |  |  |  |  |  |  | 
| 4734 |  |  |  |  |  |  | { | 
| 4735 |  |  |  |  |  |  |  | 
| 4736 |  |  |  |  |  |  | my %parse_input = ( | 
| 4737 |  |  |  |  |  |  | formatter	=> { | 
| 4738 |  |  |  |  |  |  | desired_equinox_dynamical => sub { | 
| 4739 |  |  |  |  |  |  | my ( $self, undef, @args ) = @_;	# $opt unused | 
| 4740 |  |  |  |  |  |  | if ( $args[0] ) { | 
| 4741 |  |  |  |  |  |  | $args[0] = $self->__parse_time( $args[0], 0 ); | 
| 4742 |  |  |  |  |  |  | } | 
| 4743 |  |  |  |  |  |  | return @args; | 
| 4744 |  |  |  |  |  |  | }, | 
| 4745 |  |  |  |  |  |  | format	=> sub { | 
| 4746 |  |  |  |  |  |  | my ( $self, $opt, $template, @args ) = @_; | 
| 4747 |  |  |  |  |  |  | $opt->{raw} = 1; | 
| 4748 |  |  |  |  |  |  | return ( | 
| 4749 |  |  |  |  |  |  | arg	=> \@args, | 
| 4750 |  |  |  |  |  |  | sp	=> $self, | 
| 4751 |  |  |  |  |  |  | template	=> $template, | 
| 4752 |  |  |  |  |  |  | ); | 
| 4753 |  |  |  |  |  |  | }, | 
| 4754 |  |  |  |  |  |  | }, | 
| 4755 |  |  |  |  |  |  | time_parser	=> { | 
| 4756 |  |  |  |  |  |  | base	=> sub { | 
| 4757 |  |  |  |  |  |  | my ( $self, undef, @args ) = @_;	# $opt unused | 
| 4758 |  |  |  |  |  |  | if ( @args && defined $args[0] ) { | 
| 4759 |  |  |  |  |  |  | $args[0] = $self->__parse_time( $args[0], time ); | 
| 4760 |  |  |  |  |  |  | } | 
| 4761 |  |  |  |  |  |  | return @args; | 
| 4762 |  |  |  |  |  |  | } | 
| 4763 |  |  |  |  |  |  | }, | 
| 4764 |  |  |  |  |  |  | ); | 
| 4765 |  |  |  |  |  |  |  | 
| 4766 |  |  |  |  |  |  | sub _helper_handler : Verb( changes! raw! ) { | 
| 4767 | 9 |  |  | 9 |  | 34 | my ( $self, $opt, $name, $method, @args ) = __arguments( @_ ); | 
| 4768 |  |  |  |  |  |  |  | 
| 4769 |  |  |  |  |  |  | exists $opt->{raw} | 
| 4770 | 9 | 50 |  |  |  | 42 | or $opt->{raw} = ( ! _is_interactive() ); | 
| 4771 |  |  |  |  |  |  |  | 
| 4772 | 9 | 50 |  |  |  | 35 | defined $method | 
| 4773 |  |  |  |  |  |  | or $self->wail( 'No method name specified' ); | 
| 4774 |  |  |  |  |  |  |  | 
| 4775 | 9 | 50 |  |  |  | 26 | 'config' eq $method | 
| 4776 |  |  |  |  |  |  | and return $self->_helper_config_handler( $name => $opt ); | 
| 4777 |  |  |  |  |  |  |  | 
| 4778 | 9 |  |  |  |  | 30 | my $object = $self->_helper_get_object( $name ); | 
| 4779 | 9 | 50 | 33 |  |  | 73 | $method !~ m/ \A _ /smx and $object->can( $method ) | 
| 4780 |  |  |  |  |  |  | or $self->wail("No such $name method as '$method'"); | 
| 4781 |  |  |  |  |  |  |  | 
| 4782 |  |  |  |  |  |  | @args | 
| 4783 |  |  |  |  |  |  | and $parse_input{$name} | 
| 4784 |  |  |  |  |  |  | and $parse_input{$name}{$method} | 
| 4785 | 9 | 100 | 66 |  |  | 75 | and @args = $parse_input{$name}{$method}->( $self, $opt, @args ); | 
|  |  |  | 66 |  |  |  |  | 
| 4786 |  |  |  |  |  |  | delete $opt->{raw} | 
| 4787 | 9 | 100 |  |  |  | 51 | and return $object->$method( @args ); | 
| 4788 | 5 |  |  |  |  | 26 | my @rslt = $object->decode( $method, @args ); | 
| 4789 |  |  |  |  |  |  |  | 
| 4790 | 5 | 100 |  |  |  | 93 | instance( $rslt[0], ref $object ) and return; | 
| 4791 | 2 | 50 |  |  |  | 8 | ref $rslt[0] and return $rslt[0]; | 
| 4792 | 2 |  |  |  |  | 47 | return quoter( $name, $method, @rslt ) . "\n"; | 
| 4793 | 20 |  |  | 20 |  | 228 | } | 
|  | 20 |  |  |  |  | 50 |  | 
|  | 20 |  |  |  |  | 131 |  | 
| 4794 |  |  |  |  |  |  | } | 
| 4795 |  |  |  |  |  |  |  | 
| 4796 |  |  |  |  |  |  | sub _helper_config_handler { | 
| 4797 | 0 |  |  | 0 |  | 0 | my ( $self, $name, $opt ) = @_; | 
| 4798 | 0 |  |  |  |  | 0 | my $object = $self->_helper_get_object( $name ); | 
| 4799 |  |  |  |  |  |  | my $rslt = $object->config( | 
| 4800 |  |  |  |  |  |  | changes	=> $opt->{changes}, | 
| 4801 |  |  |  |  |  |  | decode	=> ! $opt->{raw}, | 
| 4802 | 0 |  |  |  |  | 0 | ); | 
| 4803 | 0 | 0 |  |  |  | 0 | $opt->{raw} and return $rslt; | 
| 4804 | 0 |  |  |  |  | 0 | my $output = ''; | 
| 4805 | 0 |  |  |  |  | 0 | foreach my $item ( @{ $rslt } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4806 | 0 |  |  |  |  | 0 | $output .= quoter( $name, @{ $item } ) . "\n"; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4807 |  |  |  |  |  |  | } | 
| 4808 | 0 |  |  |  |  | 0 | return $output; | 
| 4809 |  |  |  |  |  |  | } | 
| 4810 |  |  |  |  |  |  |  | 
| 4811 |  |  |  |  |  |  | #	$satpass2->_iridium_status(\@status) | 
| 4812 |  |  |  |  |  |  |  | 
| 4813 |  |  |  |  |  |  | #	Updates the status of all Iridium satellites from the given | 
| 4814 |  |  |  |  |  |  | #	array, which is compatible with the second item returned by | 
| 4815 |  |  |  |  |  |  | #	Astro::SpaceTrack->iridium_status(). If no argument is passed, | 
| 4816 |  |  |  |  |  |  | #	the status is retrieved using Astro::SpaceTrack->iridium_status() | 
| 4817 |  |  |  |  |  |  |  | 
| 4818 |  |  |  |  |  |  | sub _iridium_status { | 
| 4819 | 0 |  |  | 0 |  | 0 | my ($self, $status) = @_; | 
| 4820 | 0 | 0 |  |  |  | 0 | unless ($status) { | 
| 4821 | 0 |  |  |  |  | 0 | my $st = $self->_get_spacetrack(); | 
| 4822 | 0 |  |  |  |  | 0 | (my $rslt, $status) = $st->iridium_status; | 
| 4823 | 0 | 0 |  |  |  | 0 | $rslt->is_success or $self->wail($rslt->status_line); | 
| 4824 |  |  |  |  |  |  | } | 
| 4825 |  |  |  |  |  |  |  | 
| 4826 | 0 | 0 |  |  |  | 0 | if ( ARRAY_REF eq ref $status ) { | 
| 4827 | 0 |  |  |  |  | 0 | Astro::Coord::ECI::TLE->status (clear => 'iridium'); | 
| 4828 | 0 |  |  |  |  | 0 | foreach (@$status) { | 
| 4829 | 0 |  |  |  |  | 0 | Astro::Coord::ECI::TLE->status (add => $_->[0], iridium => | 
| 4830 |  |  |  |  |  |  | $_->[4], $_->[1], $_->[3]); | 
| 4831 |  |  |  |  |  |  | } | 
| 4832 |  |  |  |  |  |  | } else { | 
| 4833 | 0 |  |  |  |  | 0 | $self->weep( | 
| 4834 |  |  |  |  |  |  | 'Portable status not passed, and unavailable from Astro::SpaceTrack' | 
| 4835 |  |  |  |  |  |  | ); | 
| 4836 |  |  |  |  |  |  | } | 
| 4837 |  |  |  |  |  |  |  | 
| 4838 | 0 |  |  |  |  | 0 | foreach my $tle (@{$self->{bodies}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4839 | 0 |  |  |  |  | 0 | $tle->rebless (); | 
| 4840 |  |  |  |  |  |  | } | 
| 4841 |  |  |  |  |  |  |  | 
| 4842 | 0 |  |  |  |  | 0 | return; | 
| 4843 |  |  |  |  |  |  |  | 
| 4844 |  |  |  |  |  |  | } | 
| 4845 |  |  |  |  |  |  |  | 
| 4846 |  |  |  |  |  |  | # _is_case_tolerant() | 
| 4847 |  |  |  |  |  |  | # Returns true if the OS supports case-tolerant file names. Yes, I know | 
| 4848 |  |  |  |  |  |  | # it's the file system that is important, but I don't have access to | 
| 4849 |  |  |  |  |  |  | # that level of detail. | 
| 4850 |  |  |  |  |  |  | { | 
| 4851 |  |  |  |  |  |  | my %os = map { $_ => 1 } qw{ darwin }; | 
| 4852 |  |  |  |  |  |  |  | 
| 4853 |  |  |  |  |  |  | sub _is_case_tolerant { | 
| 4854 |  |  |  |  |  |  | exists $os{$^O} | 
| 4855 | 0 | 0 |  | 0 |  | 0 | and return $os{$^O}; | 
| 4856 | 0 |  |  |  |  | 0 | return File::Spec->case_tolerant(); | 
| 4857 |  |  |  |  |  |  | } | 
| 4858 |  |  |  |  |  |  | } | 
| 4859 |  |  |  |  |  |  |  | 
| 4860 |  |  |  |  |  |  | #	_is_interactive() | 
| 4861 |  |  |  |  |  |  | # | 
| 4862 |  |  |  |  |  |  | #	Returns true if the dispatch() method is above us on the call | 
| 4863 |  |  |  |  |  |  | #	stack, otherwise returns false. | 
| 4864 |  |  |  |  |  |  |  | 
| 4865 | 20 |  |  | 20 |  | 13234 | use constant INTERACTIVE_CALLER => __PACKAGE__ . '::dispatch'; | 
|  | 20 |  |  |  |  | 57 |  | 
|  | 20 |  |  |  |  | 3202 |  | 
| 4866 |  |  |  |  |  |  | sub _is_interactive { | 
| 4867 | 364 |  |  | 364 |  | 538 | my $level = 0; | 
| 4868 | 364 |  |  |  |  | 2258 | while ( my @info = caller( $level ) ) { | 
| 4869 | 1520 | 100 |  |  |  | 3233 | INTERACTIVE_CALLER eq $info[3] | 
| 4870 |  |  |  |  |  |  | and return $level; | 
| 4871 | 1482 |  |  |  |  | 6254 | $level++; | 
| 4872 |  |  |  |  |  |  | } | 
| 4873 | 326 |  |  |  |  | 724 | return; | 
| 4874 |  |  |  |  |  |  | } | 
| 4875 |  |  |  |  |  |  |  | 
| 4876 |  |  |  |  |  |  | #	$self->_load_module ($module_name) | 
| 4877 |  |  |  |  |  |  |  | 
| 4878 |  |  |  |  |  |  | #	Loads the module if it has not yet been loaded. Dies if it | 
| 4879 |  |  |  |  |  |  | #	can not be loaded. | 
| 4880 |  |  |  |  |  |  |  | 
| 4881 |  |  |  |  |  |  | {	# Begin local symbol block | 
| 4882 |  |  |  |  |  |  |  | 
| 4883 |  |  |  |  |  |  | my %version; | 
| 4884 |  |  |  |  |  |  | BEGIN { | 
| 4885 | 20 |  |  | 20 |  | 134243 | %version = ( | 
| 4886 |  |  |  |  |  |  | 'Astro::SpaceTrack' => ASTRO_SPACETRACK_VERSION, | 
| 4887 |  |  |  |  |  |  | ); | 
| 4888 |  |  |  |  |  |  | } | 
| 4889 |  |  |  |  |  |  |  | 
| 4890 |  |  |  |  |  |  | sub _load_module { | 
| 4891 | 0 |  |  | 0 |  | 0 | my ($self, @module) = @_; | 
| 4892 |  |  |  |  |  |  | ARRAY_REF eq ref $module[0] | 
| 4893 | 0 | 0 |  |  |  | 0 | and @module = @{$module[0]}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4894 | 0 | 0 |  |  |  | 0 | @module or $self->weep( 'No module specified' ); | 
| 4895 | 0 |  |  |  |  | 0 | my @probs; | 
| 4896 | 0 |  |  |  |  | 0 | foreach my $module (@module) { | 
| 4897 | 0 | 0 |  |  |  | 0 | load_package ($module) or do { | 
| 4898 | 0 |  |  |  |  | 0 | push @probs, "$module needed"; | 
| 4899 | 0 |  |  |  |  | 0 | next; | 
| 4900 |  |  |  |  |  |  | }; | 
| 4901 | 0 |  |  |  |  | 0 | my $modver; | 
| 4902 | 0 | 0 | 0 |  |  | 0 | ($version{$module} && ($modver = $module->VERSION)) and do { | 
| 4903 | 0 |  |  |  |  | 0 | $modver =~ s/_//g; | 
| 4904 | 0 | 0 |  |  |  | 0 | $modver < $version{$module} and do { | 
| 4905 | 0 |  |  |  |  | 0 | push @probs, | 
| 4906 |  |  |  |  |  |  | "$module version $version{$module} needed"; | 
| 4907 | 0 |  |  |  |  | 0 | next; | 
| 4908 |  |  |  |  |  |  | }; | 
| 4909 |  |  |  |  |  |  | }; | 
| 4910 | 0 |  |  |  |  | 0 | return $module; | 
| 4911 |  |  |  |  |  |  | } | 
| 4912 |  |  |  |  |  |  | { | 
| 4913 | 0 |  |  |  |  | 0 | my $inx = 1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4914 | 0 |  |  |  |  | 0 | while (my @clr = caller($inx++)) { | 
| 4915 | 0 | 0 |  |  |  | 0 | $clr[3] eq '(eval)' and next; | 
| 4916 | 0 |  |  |  |  | 0 | my @raw = split '::', $clr[3]; | 
| 4917 | 0 | 0 |  |  |  | 0 | substr ($raw[-1], 0, 1) eq '_' and next; | 
| 4918 | 0 |  |  |  |  | 0 | push @probs, "for method $raw[-1]"; | 
| 4919 | 0 |  |  |  |  | 0 | last; | 
| 4920 |  |  |  |  |  |  | } | 
| 4921 |  |  |  |  |  |  | } | 
| 4922 | 0 |  |  |  |  | 0 | my $pfx = 'Error -'; | 
| 4923 | 0 |  |  |  |  | 0 | $self->wail(map {my $x = "$pfx $_\n"; $pfx = ' ' x 7; $x} @probs); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 4924 | 0 |  |  |  |  | 0 | return;	# Can't get here, but Perl::Critic does not know this. | 
| 4925 |  |  |  |  |  |  | } | 
| 4926 |  |  |  |  |  |  |  | 
| 4927 |  |  |  |  |  |  | }	# end local symbol block. | 
| 4928 |  |  |  |  |  |  |  | 
| 4929 |  |  |  |  |  |  | #	$output = $self->_macro($name,@args) | 
| 4930 |  |  |  |  |  |  | # | 
| 4931 |  |  |  |  |  |  | #	Execute the named macro. The @args are of course optional. | 
| 4932 |  |  |  |  |  |  |  | 
| 4933 |  |  |  |  |  |  | sub _macro { | 
| 4934 | 19 |  |  | 19 |  | 45 | my ($self, $name, @args) = @_; | 
| 4935 | 19 | 50 |  |  |  | 49 | $self->{macro}{$name} or $self->wail("No such macro as '$name'"); | 
| 4936 | 19 |  |  |  |  | 57 | my $frames = $self->_frame_push(macro => [@args]); | 
| 4937 |  |  |  |  |  |  | my $macro = $self->{frame}[-1]{macro}{$name} = | 
| 4938 | 19 |  |  |  |  | 64 | delete $self->{macro}{$name}; | 
| 4939 | 19 |  |  |  |  | 31 | my $output; | 
| 4940 |  |  |  |  |  |  | my $err; | 
| 4941 | 19 | 100 |  |  |  | 45 | my $ok = eval { | 
| 4942 | 19 |  |  |  |  | 73 | $output = $macro->execute( $name, @args ); | 
| 4943 | 18 |  |  |  |  | 43 | 1; | 
| 4944 |  |  |  |  |  |  | } or $err = $@; | 
| 4945 | 19 |  |  |  |  | 97 | $self->_frame_pop($frames); | 
| 4946 | 19 | 100 |  |  |  | 52 | $ok or $self->wail($err); | 
| 4947 | 18 |  |  |  |  | 60 | return $output; | 
| 4948 |  |  |  |  |  |  | } | 
| 4949 |  |  |  |  |  |  |  | 
| 4950 |  |  |  |  |  |  | #	$angle = _parse_angle_parts ( @parts ); | 
| 4951 |  |  |  |  |  |  | # | 
| 4952 |  |  |  |  |  |  | #	Joins parts of angles into an angle. | 
| 4953 |  |  |  |  |  |  | #	The @parts array is array references describing the parts in | 
| 4954 |  |  |  |  |  |  | #	decreasing significance, with [0] being the value, and [1] being | 
| 4955 |  |  |  |  |  |  | #	the number in the next larger part. For the first piece, [1] | 
| 4956 |  |  |  |  |  |  | #	should be the number in an entire circle. | 
| 4957 |  |  |  |  |  |  |  | 
| 4958 |  |  |  |  |  |  | sub _parse_angle_parts { | 
| 4959 | 3 |  |  | 3 |  | 12 | my @parts = @_; | 
| 4960 | 3 |  |  |  |  | 6 | my $angle = 0; | 
| 4961 | 3 |  |  |  |  | 8 | my $circle = 1; | 
| 4962 | 3 |  |  |  |  | 5 | my $places; | 
| 4963 | 3 |  |  |  |  | 9 | foreach ( @parts ) { | 
| 4964 | 9 |  |  |  |  | 14 | my ( $part, $size ) = @{ $_ }; | 
|  | 9 |  |  |  |  | 31 |  | 
| 4965 | 9 | 50 |  |  |  | 24 | defined $part or last; | 
| 4966 | 9 |  |  |  |  | 14 | $circle *= $size; | 
| 4967 | 9 |  |  |  |  | 21 | $angle = $angle * $size + $part; | 
| 4968 | 9 | 50 |  |  |  | 23 | $places = $part =~ m/ [.] ( [0-9]+ ) /smx ? length $1 : 0; | 
| 4969 |  |  |  |  |  |  | } | 
| 4970 | 3 |  |  |  |  | 10 | $angle *= 360 / $circle; | 
| 4971 | 3 | 50 |  |  |  | 23 | if ( my $mag = sprintf '%d', $circle / 360 ) { | 
| 4972 | 3 |  |  |  |  | 7 | $places += length $mag; | 
| 4973 |  |  |  |  |  |  | } | 
| 4974 | 3 |  |  |  |  | 58 | return sprintf( '%.*f', $places, $angle ) + 0; | 
| 4975 |  |  |  |  |  |  | } | 
| 4976 |  |  |  |  |  |  |  | 
| 4977 |  |  |  |  |  |  | # Documented in POD | 
| 4978 |  |  |  |  |  |  |  | 
| 4979 |  |  |  |  |  |  | sub __parse_angle { | 
| 4980 | 40 |  |  | 40 |  | 121 | my ( $self, @args ) = @_; | 
| 4981 | 40 | 100 |  |  |  | 144 | my $opt = HASH_REF eq ref $args[0] ? shift @args : {}; | 
| 4982 | 40 |  |  |  |  | 95 | my ( $angle ) = @args; | 
| 4983 | 40 | 100 |  |  |  | 110 | defined $angle or return; | 
| 4984 |  |  |  |  |  |  |  | 
| 4985 | 33 | 100 |  |  |  | 308 | if ( $angle =~ m/ : /smx ) { | 
|  |  | 100 |  |  |  |  |  | 
| 4986 |  |  |  |  |  |  |  | 
| 4987 | 2 |  |  |  |  | 31 | my ($h, $m, $s) = split ':', $angle; | 
| 4988 | 2 |  |  |  |  | 13 | return _parse_angle_parts( | 
| 4989 |  |  |  |  |  |  | [ $h => 24 ], | 
| 4990 |  |  |  |  |  |  | [ $m => 60 ], | 
| 4991 |  |  |  |  |  |  | [ $s => 60 ], | 
| 4992 |  |  |  |  |  |  | ); | 
| 4993 |  |  |  |  |  |  |  | 
| 4994 |  |  |  |  |  |  | } elsif ( $angle =~ | 
| 4995 |  |  |  |  |  |  | m{ \A ( [-+] )? ( [0-9]* ) d | 
| 4996 |  |  |  |  |  |  | ( [0-9]* (?: [.] [0-9]* )? ) (?: m | 
| 4997 |  |  |  |  |  |  | ( [0-9]* (?: [.] [0-9]* )? ) s? )? \z | 
| 4998 |  |  |  |  |  |  | }smxi ) { | 
| 4999 | 1 |  |  |  |  | 8 | my ( $sgn, $deg, $min, $sec ) = ( $1, $2, $3, $4 ); | 
| 5000 | 1 |  |  |  |  | 11 | $angle = _parse_angle_parts( | 
| 5001 |  |  |  |  |  |  | [ $deg => 360 ], | 
| 5002 |  |  |  |  |  |  | [ $min => 60 ], | 
| 5003 |  |  |  |  |  |  | [ $sec => 60 ], | 
| 5004 |  |  |  |  |  |  | ); | 
| 5005 | 1 | 50 | 33 |  |  | 8 | $sgn and '-' eq $sgn and return -$angle; | 
| 5006 | 1 |  |  |  |  | 4 | return $angle; | 
| 5007 |  |  |  |  |  |  | } | 
| 5008 |  |  |  |  |  |  |  | 
| 5009 |  |  |  |  |  |  | $opt->{accept} | 
| 5010 | 30 | 50 | 66 |  |  | 306 | or looks_like_number( $angle ) | 
| 5011 |  |  |  |  |  |  | or $self->wail( "Invalid angle '$angle'" ); | 
| 5012 |  |  |  |  |  |  |  | 
| 5013 | 30 |  |  |  |  | 114 | return $angle; | 
| 5014 |  |  |  |  |  |  | } | 
| 5015 |  |  |  |  |  |  |  | 
| 5016 |  |  |  |  |  |  | # Documented in POD | 
| 5017 |  |  |  |  |  |  | { | 
| 5018 |  |  |  |  |  |  | my %units = ( | 
| 5019 |  |  |  |  |  |  | au => AU, | 
| 5020 |  |  |  |  |  |  | ft => 0.0003048, | 
| 5021 |  |  |  |  |  |  | km => 1, | 
| 5022 |  |  |  |  |  |  | ly => LIGHTYEAR, | 
| 5023 |  |  |  |  |  |  | m => .001, | 
| 5024 |  |  |  |  |  |  | mi => 1.609344, | 
| 5025 |  |  |  |  |  |  | pc => PARSEC, | 
| 5026 |  |  |  |  |  |  | ); | 
| 5027 |  |  |  |  |  |  |  | 
| 5028 |  |  |  |  |  |  | sub __parse_distance { | 
| 5029 | 3 |  |  | 3 |  | 17 | my ($self, $string, $dfdist) = @_; | 
| 5030 | 3 | 50 |  |  |  | 15 | defined $dfdist or $dfdist = 'km'; | 
| 5031 | 3 | 50 |  |  |  | 38 | my $dfunits = $dfdist =~ s/ ( [[:alpha:]]+ ) \z //smx ? $1 : 'km'; | 
| 5032 | 3 | 50 |  |  |  | 31 | my $units = lc ( | 
| 5033 |  |  |  |  |  |  | $string =~ s/ \s* ( [[:alpha:]]+ ) \z //smx ? $1 : $dfunits ); | 
| 5034 | 3 | 50 |  |  |  | 17 | $units{$units} | 
| 5035 |  |  |  |  |  |  | or $self->wail( "Units of '$units' are unknown" ); | 
| 5036 | 3 | 50 |  |  |  | 20 | $string ne '' or $string = $dfdist; | 
| 5037 | 3 | 50 |  |  |  | 14 | looks_like_number ($string) | 
| 5038 |  |  |  |  |  |  | or $self->wail( "'$string' is not a number" ); | 
| 5039 | 3 |  |  |  |  | 20 | return $string * $units{$units}; | 
| 5040 |  |  |  |  |  |  | } | 
| 5041 |  |  |  |  |  |  | } | 
| 5042 |  |  |  |  |  |  |  | 
| 5043 |  |  |  |  |  |  | # Documented in POD | 
| 5044 |  |  |  |  |  |  |  | 
| 5045 |  |  |  |  |  |  | sub __parse_time { | 
| 5046 | 55 |  |  | 55 |  | 2112 | my ($self, $time, $default) = @_; | 
| 5047 |  |  |  |  |  |  | my $pt = $self->{time_parser} | 
| 5048 | 55 | 50 |  |  |  | 246 | or $self->wail( 'No time parser available' ); | 
| 5049 | 55 | 50 |  |  |  | 296 | $self->{time_parser}->can( 'station' ) | 
| 5050 |  |  |  |  |  |  | and $self->_set_time_parser_attribute( | 
| 5051 |  |  |  |  |  |  | station => $self->station() ); | 
| 5052 | 55 | 50 |  |  |  | 252 | if ( defined( my $time = $pt->parse( $time, $default ) ) ) { | 
| 5053 | 55 |  |  |  |  | 162 | return $time; | 
| 5054 |  |  |  |  |  |  | } | 
| 5055 | 0 |  |  |  |  | 0 | $self->wail( "Invalid time '$time'" ); | 
| 5056 | 0 |  |  |  |  | 0 | return; | 
| 5057 |  |  |  |  |  |  | } | 
| 5058 |  |  |  |  |  |  |  | 
| 5059 |  |  |  |  |  |  | #	Reset the last time set. This is called from __arguments() in | 
| 5060 |  |  |  |  |  |  | #	::Utils if the invocant is an Astro::App::Satpass2. | 
| 5061 |  |  |  |  |  |  |  | 
| 5062 |  |  |  |  |  |  | sub __parse_time_reset { | 
| 5063 | 332 |  |  | 332 |  | 694 | my ( $self ) = @_; | 
| 5064 |  |  |  |  |  |  | defined ( my $pt = $self->{time_parser} ) | 
| 5065 | 332 | 100 |  |  |  | 1049 | or return; | 
| 5066 | 311 |  |  |  |  | 1337 | $pt->reset(); | 
| 5067 | 311 |  |  |  |  | 631 | return; | 
| 5068 |  |  |  |  |  |  | } | 
| 5069 |  |  |  |  |  |  |  | 
| 5070 |  |  |  |  |  |  | #	$string = _rad2hms ($angle) | 
| 5071 |  |  |  |  |  |  |  | 
| 5072 |  |  |  |  |  |  | #	Converts the given angle in radians to hours, minutes, and | 
| 5073 |  |  |  |  |  |  | #	seconds (of right ascension, presumably) | 
| 5074 |  |  |  |  |  |  |  | 
| 5075 |  |  |  |  |  |  | sub _rad2hms { | 
| 5076 | 1 |  |  | 1 |  | 6 | my $sec = shift; | 
| 5077 | 1 |  |  |  |  | 4 | $sec *= 12 / PI; | 
| 5078 | 1 |  |  |  |  | 5 | my $hr = floor( $sec ); | 
| 5079 | 1 |  |  |  |  | 8 | $sec = ( $sec - $hr ) * 60; | 
| 5080 | 1 |  |  |  |  | 11 | my $min = floor( $sec ); | 
| 5081 | 1 |  |  |  |  | 8 | $sec = ( $sec - $min ) * 60; | 
| 5082 | 1 |  |  |  |  | 11 | my $rslt = sprintf '%2d:%02d:%02d', $hr, $min, floor( $sec + .5 ); | 
| 5083 | 1 |  |  |  |  | 9 | return $rslt; | 
| 5084 |  |  |  |  |  |  | } | 
| 5085 |  |  |  |  |  |  |  | 
| 5086 |  |  |  |  |  |  | #	$line = $self->_read_continuation( $in, $error_message ); | 
| 5087 |  |  |  |  |  |  | # | 
| 5088 |  |  |  |  |  |  | #	Acquire a line from $in, which must be a code reference taking | 
| 5089 |  |  |  |  |  |  | #	the prompt as an argument. If $in is not a code reference, or if | 
| 5090 |  |  |  |  |  |  | #	it returns undef, we wail() with the error message.  Otherwise | 
| 5091 |  |  |  |  |  |  | #	we return the line read. I expect this to be used only by | 
| 5092 |  |  |  |  |  |  | #	__tokenize(). | 
| 5093 |  |  |  |  |  |  |  | 
| 5094 |  |  |  |  |  |  | sub _read_continuation { | 
| 5095 | 15 |  |  | 15 |  | 52 | my ( $self, $in, $error ) = @_; | 
| 5096 |  |  |  |  |  |  | $in and defined( my $more = $in->( | 
| 5097 |  |  |  |  |  |  | my $prompt = $self->get( 'continuation_prompt' ) ) ) | 
| 5098 | 15 | 100 | 66 |  |  | 100 | or do { | 
| 5099 | 1 | 50 |  |  |  | 3 | $error or return; | 
| 5100 | 1 | 50 |  |  |  | 5 | ref $error eq CODE_REF | 
| 5101 |  |  |  |  |  |  | and return $error->(); | 
| 5102 | 1 |  |  |  |  | 3 | $self->wail( $error ); | 
| 5103 |  |  |  |  |  |  | }; | 
| 5104 | 14 | 50 |  |  |  | 91 | $self->{echo} and $self->whinge( $prompt, $more ); | 
| 5105 | 14 | 100 |  |  |  | 91 | $more =~ m/ \n \z /smx or $more .= "\n"; | 
| 5106 | 14 |  |  |  |  | 48 | return $more; | 
| 5107 |  |  |  |  |  |  | } | 
| 5108 |  |  |  |  |  |  |  | 
| 5109 |  |  |  |  |  |  | # my ( $old_obj ) = $self->_replace_in_sky( $name, $new_obj ); | 
| 5110 |  |  |  |  |  |  | # This is restricted to objects constructed via {sky_class}. | 
| 5111 |  |  |  |  |  |  | # The return is an array containing the replaced body, or nothing if | 
| 5112 |  |  |  |  |  |  | # the body was not found. The $new_obj is optional; if not provided a | 
| 5113 |  |  |  |  |  |  | # new object is created. | 
| 5114 |  |  |  |  |  |  | sub _replace_in_sky { | 
| 5115 | 0 |  |  | 0 |  | 0 | my ( $self, $name, $new_obj ) = @_; | 
| 5116 |  |  |  |  |  |  | $new_obj | 
| 5117 | 0 | 0 | 0 |  |  | 0 | or $self->{sky_class}{ fold_case( $name ) } | 
| 5118 |  |  |  |  |  |  | or $self->weep( "Can not replace $name; no class defined" ); | 
| 5119 | 0 | 0 |  |  |  | 0 | defined( my $inx = $self->_find_in_sky( $name ) ) | 
| 5120 |  |  |  |  |  |  | or return; | 
| 5121 | 0 |  | 0 |  |  | 0 | return splice @{ $self->{sky} }, $inx, $inx + 1, | 
|  | 0 |  |  |  |  | 0 |  | 
| 5122 |  |  |  |  |  |  | $new_obj || $self->_sky_object( $name ); | 
| 5123 |  |  |  |  |  |  | } | 
| 5124 |  |  |  |  |  |  |  | 
| 5125 |  |  |  |  |  |  | #	$self->_rewrite_level1_command( $buffer, $context ); | 
| 5126 |  |  |  |  |  |  | # | 
| 5127 |  |  |  |  |  |  | #	This method rewrites a level1 command to its current form. The | 
| 5128 |  |  |  |  |  |  | #	arguments are the buffer containing the command, and an | 
| 5129 |  |  |  |  |  |  | #	initially-empty hash reference, which the method will use to | 
| 5130 |  |  |  |  |  |  | #	preserve context across lines of command. NOTE that more than | 
| 5131 |  |  |  |  |  |  | #	one rewritten command may be returned (e.g. 'almanac' into | 
| 5132 |  |  |  |  |  |  | #	( 'location', 'almanac' ). | 
| 5133 |  |  |  |  |  |  |  | 
| 5134 |  |  |  |  |  |  | { | 
| 5135 |  |  |  |  |  |  |  | 
| 5136 |  |  |  |  |  |  | my %level1_map = ( | 
| 5137 |  |  |  |  |  |  | almanac	=> sub { | 
| 5138 |  |  |  |  |  |  | return ( 'location', $_[0] ); | 
| 5139 |  |  |  |  |  |  | }, | 
| 5140 |  |  |  |  |  |  | flare	=> sub { | 
| 5141 |  |  |  |  |  |  | local $_ = $_[0]; | 
| 5142 |  |  |  |  |  |  | s/ (?<= \s ) - ( am|pm|day ) \b /-no$1/sxmg; | 
| 5143 |  |  |  |  |  |  | return $_; | 
| 5144 |  |  |  |  |  |  | }, | 
| 5145 |  |  |  |  |  |  | pass	=> sub { | 
| 5146 |  |  |  |  |  |  | return ( 'location', $_[0] ); | 
| 5147 |  |  |  |  |  |  | }, | 
| 5148 |  |  |  |  |  |  | ); | 
| 5149 |  |  |  |  |  |  |  | 
| 5150 |  |  |  |  |  |  | my %level1_requote = ( | 
| 5151 |  |  |  |  |  |  | # In a macro definition: | 
| 5152 |  |  |  |  |  |  | macro	=> { | 
| 5153 |  |  |  |  |  |  | # In single-quoted strings, | 
| 5154 |  |  |  |  |  |  | q{'}	=> sub { | 
| 5155 |  |  |  |  |  |  | # escaped interpolations and double quotes may be | 
| 5156 |  |  |  |  |  |  | # unescaped, | 
| 5157 |  |  |  |  |  |  | s{ (?: \A | (? | 
| 5158 |  |  |  |  |  |  | }{$1$2}sxmg; | 
| 5159 |  |  |  |  |  |  | # and the string remains single-quoted. | 
| 5160 |  |  |  |  |  |  | $_ = qq{'$_'}; | 
| 5161 |  |  |  |  |  |  | return; | 
| 5162 |  |  |  |  |  |  | }, | 
| 5163 |  |  |  |  |  |  | # In double-quoted strings, | 
| 5164 |  |  |  |  |  |  | q{"}	=> sub { | 
| 5165 |  |  |  |  |  |  | # escaped interpolations and double quotes may be | 
| 5166 |  |  |  |  |  |  | # unescaped, | 
| 5167 |  |  |  |  |  |  | s{ (?: \A | (? | 
| 5168 |  |  |  |  |  |  | }{$1$2}sxmg; | 
| 5169 |  |  |  |  |  |  | # unescaped single quotes become double quotes, | 
| 5170 |  |  |  |  |  |  | s/ (?: \A | (? | 
| 5171 |  |  |  |  |  |  | # and the string becomes single-quoted. | 
| 5172 |  |  |  |  |  |  | $_ = qq{'$_'}; | 
| 5173 |  |  |  |  |  |  | return; | 
| 5174 |  |  |  |  |  |  | }, | 
| 5175 |  |  |  |  |  |  | }, | 
| 5176 |  |  |  |  |  |  | # Anywhere else | 
| 5177 |  |  |  |  |  |  | ''	=> { | 
| 5178 |  |  |  |  |  |  | # In single-quoted strings, | 
| 5179 |  |  |  |  |  |  | q{'}	=> sub { | 
| 5180 |  |  |  |  |  |  | # unescaped double quotes must be escaped, | 
| 5181 |  |  |  |  |  |  | s/ (?: \A | (? | 
| 5182 |  |  |  |  |  |  | # escaped single quotes may be unescaped, | 
| 5183 |  |  |  |  |  |  | s/ (?: \A | (? | 
| 5184 |  |  |  |  |  |  | # and the string becomes double-quoted. | 
| 5185 |  |  |  |  |  |  | $_ = qq{"$_"}; | 
| 5186 |  |  |  |  |  |  | return; | 
| 5187 |  |  |  |  |  |  | }, | 
| 5188 |  |  |  |  |  |  | # In double-quoted strings, | 
| 5189 |  |  |  |  |  |  | q{"}	=> sub { | 
| 5190 |  |  |  |  |  |  | # no changes need to be made. | 
| 5191 |  |  |  |  |  |  | $_ = qq{"$_"}; | 
| 5192 |  |  |  |  |  |  | return; | 
| 5193 |  |  |  |  |  |  | }, | 
| 5194 |  |  |  |  |  |  | }, | 
| 5195 |  |  |  |  |  |  | ); | 
| 5196 |  |  |  |  |  |  |  | 
| 5197 |  |  |  |  |  |  | sub _rewrite_level1_command { | 
| 5198 | 19 |  |  | 19 |  | 49 | my ( undef, $buffer, $context ) = @_;	# Invocant unused | 
| 5199 |  |  |  |  |  |  |  | 
| 5200 | 19 |  |  |  |  | 30 | my $command = delete $context->{command}; | 
| 5201 |  |  |  |  |  |  |  | 
| 5202 | 19 | 100 |  |  |  | 51 | defined $buffer | 
| 5203 |  |  |  |  |  |  | or return $buffer; | 
| 5204 | 12 | 50 |  |  |  | 57 | $buffer =~ m/ \A \s* \z /sxm | 
| 5205 |  |  |  |  |  |  | and return $buffer; | 
| 5206 | 12 | 50 |  |  |  | 41 | $buffer =~ s/ \A \s* [#] 2 [#] \s* //sxm | 
| 5207 |  |  |  |  |  |  | and return $buffer; | 
| 5208 | 12 | 50 |  |  |  | 31 | $buffer =~ m/ \A \s* [#] /sxm | 
| 5209 |  |  |  |  |  |  | and return $buffer; | 
| 5210 |  |  |  |  |  |  |  | 
| 5211 | 12 | 50 |  |  |  | 34 | if ( ! defined $command ) { | 
| 5212 | 12 | 100 |  |  |  | 46 | $buffer =~ m/ \A \s* ( \w+ ) /sxm | 
| 5213 |  |  |  |  |  |  | or return $buffer; | 
| 5214 | 11 |  |  |  |  | 30 | $command = $1; | 
| 5215 |  |  |  |  |  |  | } | 
| 5216 | 11 |  |  |  |  | 20 | my $append = ''; | 
| 5217 | 11 | 100 |  |  |  | 87 | $buffer =~ s/ ( \s* \\? \n ) //sxm | 
| 5218 |  |  |  |  |  |  | and $append = $1; | 
| 5219 |  |  |  |  |  |  | $append =~ m/ \\ /sxm | 
| 5220 | 11 | 50 |  |  |  | 33 | and $context->{command} = $command; | 
| 5221 |  |  |  |  |  |  |  | 
| 5222 | 11 |  | 66 |  |  | 36 | my $handler = $level1_requote{$command} || $level1_requote{''}; | 
| 5223 | 11 |  |  |  |  | 52 | my ( $this_quote, $start_pos ); | 
| 5224 | 11 |  |  |  |  | 93 | while ( $buffer =~ m/ (?: \A | (? | 
| 5225 |  |  |  |  |  |  | ) { | 
| 5226 | 22 | 100 |  |  |  | 85 | if ( ! defined $start_pos ) { | 
|  |  | 100 |  |  |  |  |  | 
| 5227 | 9 |  |  |  |  | 30 | $start_pos = $+[0] - 1; | 
| 5228 | 9 |  |  |  |  | 78 | $this_quote = $1; | 
| 5229 |  |  |  |  |  |  | } elsif ( $1 eq $this_quote ) { | 
| 5230 | 9 |  |  |  |  | 25 | my $length = $+[0] - $start_pos; | 
| 5231 | 9 |  |  |  |  | 37 | local $_ = substr $buffer, $start_pos + 1, $length - 2; | 
| 5232 | 9 |  |  |  |  | 31 | $handler->{$this_quote}->(); | 
| 5233 | 9 |  |  |  |  | 57 | substr $buffer, $start_pos, $length, $_; | 
| 5234 | 9 |  |  |  |  | 26 | pos( $buffer ) = $start_pos + length $_; | 
| 5235 | 9 |  |  |  |  | 41 | $start_pos = undef; | 
| 5236 |  |  |  |  |  |  | } | 
| 5237 |  |  |  |  |  |  | } | 
| 5238 |  |  |  |  |  |  |  | 
| 5239 | 11 | 100 |  |  |  | 64 | my $code = $level1_map{$command} | 
| 5240 |  |  |  |  |  |  | or return $buffer . $append; | 
| 5241 |  |  |  |  |  |  |  | 
| 5242 | 3 |  |  |  |  | 15 | my @rslt = $code->( $buffer ); | 
| 5243 | 3 |  |  |  |  | 9 | $rslt[-1] .= $append; | 
| 5244 | 3 |  |  |  |  | 12 | return @rslt; | 
| 5245 |  |  |  |  |  |  |  | 
| 5246 |  |  |  |  |  |  | } | 
| 5247 |  |  |  |  |  |  | } | 
| 5248 |  |  |  |  |  |  |  | 
| 5249 |  |  |  |  |  |  | #	$self->_rewrite_level1_macros(); | 
| 5250 |  |  |  |  |  |  | # | 
| 5251 |  |  |  |  |  |  | #	This method rewrites all macros defined by a satpass | 
| 5252 |  |  |  |  |  |  | #	initialization file (as opposed to a satpass2 initialization | 
| 5253 |  |  |  |  |  |  | #	file) to be satpass2-compatible. It also clears the level1 flag | 
| 5254 |  |  |  |  |  |  | #	so that the satpass-compatible functionality is not invoked. | 
| 5255 |  |  |  |  |  |  | # | 
| 5256 |  |  |  |  |  |  | #	Specifically it: | 
| 5257 |  |  |  |  |  |  | #	* Inserts a 'location' command before 'almanac' and 'pass'; | 
| 5258 |  |  |  |  |  |  | #	* Changes the senses of the -am, -day, and -pm options in | 
| 5259 |  |  |  |  |  |  | #	  'flare'; | 
| 5260 |  |  |  |  |  |  | #	* Removes delegated attributes from 'localize', replacing them | 
| 5261 |  |  |  |  |  |  | #	  with a localization of the helper object. | 
| 5262 |  |  |  |  |  |  | # | 
| 5263 |  |  |  |  |  |  | #	This method goes away when the satpass functionality does. | 
| 5264 |  |  |  |  |  |  |  | 
| 5265 |  |  |  |  |  |  | { | 
| 5266 |  |  |  |  |  |  | my %helper_map = ( | 
| 5267 |  |  |  |  |  |  | date_format	=> { | 
| 5268 |  |  |  |  |  |  | helper	=> 'formatter',		# Helper obj attr. Req'd. | 
| 5269 |  |  |  |  |  |  | }, | 
| 5270 |  |  |  |  |  |  | desired_equinox_dynamical	=> { | 
| 5271 |  |  |  |  |  |  | helper	=> 'formatter', | 
| 5272 |  |  |  |  |  |  | }, | 
| 5273 |  |  |  |  |  |  | gmt		=> { | 
| 5274 |  |  |  |  |  |  | helper	=> 'formatter', | 
| 5275 |  |  |  |  |  |  | }, | 
| 5276 |  |  |  |  |  |  | local_coord	=> { | 
| 5277 |  |  |  |  |  |  | helper	=> 'formatter', | 
| 5278 |  |  |  |  |  |  | }, | 
| 5279 |  |  |  |  |  |  | time_format	=> { | 
| 5280 |  |  |  |  |  |  | helper	=> 'formatter', | 
| 5281 |  |  |  |  |  |  | }, | 
| 5282 |  |  |  |  |  |  | ); | 
| 5283 |  |  |  |  |  |  |  | 
| 5284 |  |  |  |  |  |  | my %filter = ( | 
| 5285 |  |  |  |  |  |  | almanac	=> sub { | 
| 5286 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5287 |  |  |  |  |  |  | return ( 'location', $line ); | 
| 5288 |  |  |  |  |  |  | }, | 
| 5289 |  |  |  |  |  |  | flare	=> sub { | 
| 5290 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5291 |  |  |  |  |  |  | $line =~ s/ (?<= \s ) - (am|day|pm) \b /-no$1/smx; | 
| 5292 |  |  |  |  |  |  | return $line; | 
| 5293 |  |  |  |  |  |  | }, | 
| 5294 |  |  |  |  |  |  | localize	=> sub { | 
| 5295 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5296 |  |  |  |  |  |  | my @things = split qr{ \s+ }smx, $line; | 
| 5297 |  |  |  |  |  |  | my @output; | 
| 5298 |  |  |  |  |  |  | my %duplicate; | 
| 5299 |  |  |  |  |  |  | foreach my $token ( @things ) { | 
| 5300 |  |  |  |  |  |  | $helper_map{$token} | 
| 5301 |  |  |  |  |  |  | and $token = $helper_map{$token}{helper}; | 
| 5302 |  |  |  |  |  |  | $duplicate{$token}++ or push @output, $token; | 
| 5303 |  |  |  |  |  |  | } | 
| 5304 |  |  |  |  |  |  | return join ' ', @output; | 
| 5305 |  |  |  |  |  |  | }, | 
| 5306 |  |  |  |  |  |  | pass	=> sub { | 
| 5307 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5308 |  |  |  |  |  |  | return ( 'location', $line ); | 
| 5309 |  |  |  |  |  |  | }, | 
| 5310 |  |  |  |  |  |  | set	=> sub { | 
| 5311 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5312 |  |  |  |  |  |  | my @output = [ 'fubar' ];	# Prime the pump. | 
| 5313 |  |  |  |  |  |  | my @input = Text::ParseWords::quotewords( qr{ \s+ }smx, 1, | 
| 5314 |  |  |  |  |  |  | $line ); | 
| 5315 |  |  |  |  |  |  | shift @input; | 
| 5316 |  |  |  |  |  |  | while ( @input ) { | 
| 5317 |  |  |  |  |  |  | my ( $attr, $val ) = splice @input, 0, 2; | 
| 5318 |  |  |  |  |  |  | if ( my $helper = $helper_map{$attr} ) { | 
| 5319 |  |  |  |  |  |  | push @output, [ $helper->{helper}, | 
| 5320 |  |  |  |  |  |  | # not quoter( $val ) here, because presumably it | 
| 5321 |  |  |  |  |  |  | # is already quoted if it needs to be. | 
| 5322 |  |  |  |  |  |  | $helper->{attribute} || $attr, $val ]; | 
| 5323 |  |  |  |  |  |  | } else { | 
| 5324 |  |  |  |  |  |  | 'set' eq $output[-1][0] | 
| 5325 |  |  |  |  |  |  | or push @output, [ 'set' ]; | 
| 5326 |  |  |  |  |  |  | # not quoter( $val ) here, because presumably it is | 
| 5327 |  |  |  |  |  |  | # already quoted if it needs to be. | 
| 5328 |  |  |  |  |  |  | push @{ $output[-1] }, $attr, $val; | 
| 5329 |  |  |  |  |  |  | } | 
| 5330 |  |  |  |  |  |  | } | 
| 5331 |  |  |  |  |  |  | shift @output;	# Get rid of the pump priming. | 
| 5332 |  |  |  |  |  |  | return ( map { join ' ', @{ $_ } } @output ); | 
| 5333 |  |  |  |  |  |  | }, | 
| 5334 |  |  |  |  |  |  | st	=> sub { | 
| 5335 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5336 |  |  |  |  |  |  | m/ \A \s* st \s+ localize \b /smx | 
| 5337 |  |  |  |  |  |  | and return $line; | 
| 5338 |  |  |  |  |  |  | $line =~ s/ \b st \b /spacetrack/smx; | 
| 5339 |  |  |  |  |  |  | return $line; | 
| 5340 |  |  |  |  |  |  | }, | 
| 5341 |  |  |  |  |  |  | show	=> sub { | 
| 5342 |  |  |  |  |  |  | my ( undef, $line ) = @_;		# $verb unused | 
| 5343 |  |  |  |  |  |  | my @output = [ 'fubar' ]; | 
| 5344 |  |  |  |  |  |  | my @input = split qr{ \s+ }smx, $line; | 
| 5345 |  |  |  |  |  |  | shift @input; | 
| 5346 |  |  |  |  |  |  | foreach my $attr ( @input ) { | 
| 5347 |  |  |  |  |  |  | if ( my $helper = $helper_map{$attr} ) { | 
| 5348 |  |  |  |  |  |  | push @output, [ $helper->{helper}, | 
| 5349 |  |  |  |  |  |  | $helper->{attribute} || $attr ]; | 
| 5350 |  |  |  |  |  |  | } else { | 
| 5351 |  |  |  |  |  |  | 'show' eq $output[-1][0] | 
| 5352 |  |  |  |  |  |  | or push @output, [ 'show' ]; | 
| 5353 |  |  |  |  |  |  | push @{ $output[-1] }, $attr; | 
| 5354 |  |  |  |  |  |  | } | 
| 5355 |  |  |  |  |  |  | } | 
| 5356 |  |  |  |  |  |  | shift @output; | 
| 5357 |  |  |  |  |  |  | return ( map { join ' ', @{ $_ } } @output ); | 
| 5358 |  |  |  |  |  |  | }, | 
| 5359 |  |  |  |  |  |  | ); | 
| 5360 |  |  |  |  |  |  |  | 
| 5361 |  |  |  |  |  |  | # Called by macro object's __level1_rewrite(). | 
| 5362 |  |  |  |  |  |  | sub __rewrite_level1_macro_def { | 
| 5363 | 8 |  |  | 8 |  | 16 | my ( $self, $name, $args ) = @_; | 
| 5364 |  |  |  |  |  |  |  | 
| 5365 | 8 |  |  |  |  | 12 | my ( $rewrote, @rslt ); | 
| 5366 | 8 |  |  |  |  | 22 | foreach ( @{ $args } ) { | 
|  | 8 |  |  |  |  | 19 |  | 
| 5367 | 8 | 100 | 100 |  |  | 83 | if ( m/ ( \S+ ) /smx | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 5368 |  |  |  |  |  |  | and ( not $self->{macro}{$1} | 
| 5369 |  |  |  |  |  |  | or $1 eq $name ) | 
| 5370 |  |  |  |  |  |  | and my $code = $filter{$1} ) { | 
| 5371 | 7 |  |  |  |  | 42 | push @rslt, $code->( $1, $_ ); | 
| 5372 | 7 |  |  |  |  | 17 | $rewrote++; | 
| 5373 |  |  |  |  |  |  | } else { | 
| 5374 | 1 |  |  |  |  | 3 | push @rslt, $_; | 
| 5375 |  |  |  |  |  |  | } | 
| 5376 |  |  |  |  |  |  | } | 
| 5377 |  |  |  |  |  |  |  | 
| 5378 | 8 | 100 |  |  |  | 32 | return $rewrote ? \@rslt : $args; | 
| 5379 |  |  |  |  |  |  | } | 
| 5380 |  |  |  |  |  |  |  | 
| 5381 |  |  |  |  |  |  | sub _rewrite_level1_macros { | 
| 5382 | 4 |  |  | 4 |  | 9 | my ( $self ) = @_; | 
| 5383 |  |  |  |  |  |  |  | 
| 5384 | 4 |  |  |  |  | 8 | foreach my $macro ( values %{ $self->{macro} } ) { | 
|  | 4 |  |  |  |  | 12 |  | 
| 5385 | 8 |  |  |  |  | 25 | $macro->__level1_rewrite(); | 
| 5386 |  |  |  |  |  |  | } | 
| 5387 |  |  |  |  |  |  |  | 
| 5388 | 4 |  |  |  |  | 8 | return; | 
| 5389 |  |  |  |  |  |  | } | 
| 5390 |  |  |  |  |  |  | } | 
| 5391 |  |  |  |  |  |  |  | 
| 5392 |  |  |  |  |  |  | #	@coordinates = $self->_simbad4 ($query) | 
| 5393 |  |  |  |  |  |  |  | 
| 5394 |  |  |  |  |  |  | #	Look up the given star in the SIMBAD catalog. This assumes | 
| 5395 |  |  |  |  |  |  | #	SIMBAD 4. | 
| 5396 |  |  |  |  |  |  |  | 
| 5397 |  |  |  |  |  |  | #	We die on any error. | 
| 5398 |  |  |  |  |  |  |  | 
| 5399 |  |  |  |  |  |  | sub _simbad4 { | 
| 5400 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 5401 | 0 |  |  |  |  | 0 | $self->_load_module ('Astro::SIMBAD::Client'); | 
| 5402 | 0 |  |  |  |  | 0 | my $query = shift; | 
| 5403 |  |  |  |  |  |  | my $simbad = Astro::SIMBAD::Client->new ( | 
| 5404 |  |  |  |  |  |  | format => {txt => 'FORMAT_TXT_SIMPLE_BASIC'}, | 
| 5405 |  |  |  |  |  |  | parser => { | 
| 5406 |  |  |  |  |  |  | script	=> 'Parse_TXT_Simple', | 
| 5407 |  |  |  |  |  |  | txt		=> 'Parse_TXT_Simple', | 
| 5408 |  |  |  |  |  |  | }, | 
| 5409 |  |  |  |  |  |  | server => $self->{simbad_url}, | 
| 5410 | 0 |  |  |  |  | 0 | type => 'txt', | 
| 5411 |  |  |  |  |  |  | ); | 
| 5412 |  |  |  |  |  |  | # I prefer script() to query() these days because the former does | 
| 5413 |  |  |  |  |  |  | # not require SOAP::Lite, which seems to be getting flakier as time | 
| 5414 |  |  |  |  |  |  | # goes on. | 
| 5415 |  |  |  |  |  |  | # TODO get rid of $fmt =~ s/// once I massage | 
| 5416 |  |  |  |  |  |  | # FORMAT_TXT_SIMPLE_BASIC in Astro::SIMBAD::Client | 
| 5417 |  |  |  |  |  |  | #   my @rslt = $simbad->query (id => $query) | 
| 5418 | 0 |  |  |  |  | 0 | my $fmt = Astro::SIMBAD::Client->FORMAT_TXT_SIMPLE_BASIC(); | 
| 5419 | 0 |  |  |  |  | 0 | $fmt =~ s/ \n //smxg; | 
| 5420 | 0 | 0 |  |  |  | 0 | my @rslt = $simbad->script( <<"EOD" ) | 
| 5421 |  |  |  |  |  |  | format obj "$fmt" | 
| 5422 |  |  |  |  |  |  | query id $query | 
| 5423 |  |  |  |  |  |  | EOD | 
| 5424 |  |  |  |  |  |  | or $self->wail("No entry found for $query"); | 
| 5425 | 0 | 0 |  |  |  | 0 | @rslt > 1 | 
| 5426 |  |  |  |  |  |  | and $self->wail("More than one entry found for $query"); | 
| 5427 | 0 | 0 | 0 |  |  | 0 | @rslt = map {$rslt[0]{$_} eq '~' ? 0 : $rslt[0]{$_} || 0} qw{ | 
|  | 0 |  |  |  |  | 0 |  | 
| 5428 |  |  |  |  |  |  | ra dec plx pmra pmdec radial}; | 
| 5429 | 0 | 0 | 0 |  |  | 0 | ($rslt[0] && $rslt[1]) | 
| 5430 |  |  |  |  |  |  | or $self->wail("No position returned by $query"); | 
| 5431 | 0 | 0 |  |  |  | 0 | $rslt[2] = $rslt[2] ? 1000 / $rslt[2] : 10000; | 
| 5432 | 0 | 0 |  |  |  | 0 | $rslt[3] and $rslt[3] /= 1000; | 
| 5433 | 0 | 0 |  |  |  | 0 | $rslt[4] and $rslt[4] /= 1000; | 
| 5434 | 0 | 0 |  |  |  | 0 | return wantarray ? @rslt : join ' ', @rslt; | 
| 5435 |  |  |  |  |  |  | } | 
| 5436 |  |  |  |  |  |  |  | 
| 5437 |  |  |  |  |  |  | sub _templates_to_options { | 
| 5438 | 24 |  |  | 24 |  | 106 | my ( $self, $name, $opt ) = @_; | 
| 5439 | 24 |  |  |  |  | 102 | $opt->{_template} = $name; | 
| 5440 |  |  |  |  |  |  | my $code = sub { | 
| 5441 | 5 |  |  | 5 |  | 3135 | my ( $opt_name, $opt_value ) = @_; | 
| 5442 | 5 | 50 |  |  |  | 92 | $opt->{_template} = $opt_value ? "${name}_$opt_name" : $name; | 
| 5443 | 5 |  |  |  |  | 44 | return; | 
| 5444 | 24 |  |  |  |  | 200 | }; | 
| 5445 | 24 |  |  |  |  | 240 | my $re = qr< \A \Q$name\E _ ( \w+ ) \z >smx; | 
| 5446 | 24 |  |  |  |  | 55 | my @rslt; | 
| 5447 | 24 |  |  |  |  | 95 | my $fmtr = $self->get( 'formatter' ); | 
| 5448 | 24 | 50 |  |  |  | 257 | if ( $fmtr->can( '__list_templates' ) ) { | 
| 5449 | 24 |  |  |  |  | 149 | foreach ( $fmtr->__list_templates() ) { | 
| 5450 | 672 | 100 |  |  |  | 2004 | $_ =~ $re | 
| 5451 |  |  |  |  |  |  | or next; | 
| 5452 | 44 |  |  |  |  | 203 | push @rslt, "$1!", $code; | 
| 5453 |  |  |  |  |  |  | } | 
| 5454 |  |  |  |  |  |  | } | 
| 5455 | 24 |  |  |  |  | 434 | return @rslt; | 
| 5456 |  |  |  |  |  |  | } | 
| 5457 |  |  |  |  |  |  |  | 
| 5458 |  |  |  |  |  |  | #	($tokens, $redirect) = $self->__tokenize( | 
| 5459 |  |  |  |  |  |  | #		{option => $value}, $buffer, [$arg0 ...]); | 
| 5460 |  |  |  |  |  |  | # | 
| 5461 |  |  |  |  |  |  | #	This method tokenizes the buffer. The options hash may be | 
| 5462 |  |  |  |  |  |  | #	omitted, in which case the $buffer to be tokenized is the first | 
| 5463 |  |  |  |  |  |  | #	argument. After the buffer is an optional reference to an array | 
| 5464 |  |  |  |  |  |  | #	of arguments to be substituted in. | 
| 5465 |  |  |  |  |  |  | # | 
| 5466 |  |  |  |  |  |  | #	This method attempts to parse and tokenize the buffer in a way | 
| 5467 |  |  |  |  |  |  | #	similar to the bash shell. That is, parameters are interpolated | 
| 5468 |  |  |  |  |  |  | #	inside double quotes but not single quotes, tilde expansion | 
| 5469 |  |  |  |  |  |  | #	takes place unless quoted, and spaces delimit tokens only when | 
| 5470 |  |  |  |  |  |  | #	occurring outside quotes. | 
| 5471 |  |  |  |  |  |  | # | 
| 5472 |  |  |  |  |  |  | #	The back slash character ('\') is an escape character. Inside | 
| 5473 |  |  |  |  |  |  | #	single quotes only the back slash itself and a single quote may | 
| 5474 |  |  |  |  |  |  | #	be escaped. Otherwise, anything can be escaped. | 
| 5475 |  |  |  |  |  |  | # | 
| 5476 |  |  |  |  |  |  | #	The returns are a reference to an array of tokens found, and a | 
| 5477 |  |  |  |  |  |  | #	reference to a hash of redirections found. This hash will have | 
| 5478 |  |  |  |  |  |  | #	zero or more of the keys '>' (standard output redirection) and | 
| 5479 |  |  |  |  |  |  | #	'<' (standard input redirection. The value of each key will be a | 
| 5480 |  |  |  |  |  |  | #	reference to a hash containing keys 'mode' ('>' or '>>' for | 
| 5481 |  |  |  |  |  |  | #	output, '<' or '<<' for input) and 'name' (normally the file | 
| 5482 |  |  |  |  |  |  | #	name). | 
| 5483 |  |  |  |  |  |  | # | 
| 5484 |  |  |  |  |  |  | #	The recognized options are: | 
| 5485 |  |  |  |  |  |  | # | 
| 5486 |  |  |  |  |  |  | #	    single => 1 | 
| 5487 |  |  |  |  |  |  | #		causes the buffer to be interpreted as a single token. | 
| 5488 |  |  |  |  |  |  | # | 
| 5489 |  |  |  |  |  |  | #	    noredirect => 1 | 
| 5490 |  |  |  |  |  |  | #		causes redirects to be illegal. | 
| 5491 |  |  |  |  |  |  | # | 
| 5492 |  |  |  |  |  |  | #	If noredirect is specified, only the $tokens reference is | 
| 5493 |  |  |  |  |  |  | #	returned. If noredirect and single are both specified, the | 
| 5494 |  |  |  |  |  |  | #	parsed and interpolated token is returned. | 
| 5495 |  |  |  |  |  |  | # | 
| 5496 |  |  |  |  |  |  | #	If interpolation is being done, an unescaped dollar sign | 
| 5497 |  |  |  |  |  |  | #	introduces the interpolation. This works pretty much the same | 
| 5498 |  |  |  |  |  |  | #	way as under bash: if the first character after the dollar sign | 
| 5499 |  |  |  |  |  |  | #	is a left curly bracket, everything to the corresponding right | 
| 5500 |  |  |  |  |  |  | #	curly bracked specifies the interpolation; if not, the rule is | 
| 5501 |  |  |  |  |  |  | #	that word characters specify the interpolation. | 
| 5502 |  |  |  |  |  |  | # | 
| 5503 |  |  |  |  |  |  | #	A number (i.e. $1) specifies interpolation of an argument. | 
| 5504 |  |  |  |  |  |  | #	Arguments are numbered starting at 1. | 
| 5505 |  |  |  |  |  |  | # | 
| 5506 |  |  |  |  |  |  | #	Otherwise, if the interpolation names an attribute, the value of | 
| 5507 |  |  |  |  |  |  | #	that attribute is interpolated in, otherwise the named | 
| 5508 |  |  |  |  |  |  | #	environment variable is interpolated in. | 
| 5509 |  |  |  |  |  |  | # | 
| 5510 |  |  |  |  |  |  | #	Most of the fancier forms of interpolation are suported. In the | 
| 5511 |  |  |  |  |  |  | #	following, word is expanded by recursively calling __tokenize | 
| 5512 |  |  |  |  |  |  | #	with options {single => 1, noredirect => 1}. But unlike bash, we | 
| 5513 |  |  |  |  |  |  | #	make no distinction between unset or null. The ':' can be | 
| 5514 |  |  |  |  |  |  | #	omitted before the '-', '=', '?' or '+', but it does not change | 
| 5515 |  |  |  |  |  |  | #	the functionality. | 
| 5516 |  |  |  |  |  |  | # | 
| 5517 |  |  |  |  |  |  | #	${parameter:-word} causes the given word to be substituted if | 
| 5518 |  |  |  |  |  |  | #	the parameter is undefined. | 
| 5519 |  |  |  |  |  |  | # | 
| 5520 |  |  |  |  |  |  | #	${parameter:=word} is the same as above, but also causes the | 
| 5521 |  |  |  |  |  |  | #	word to be assigned to the parameter if it is unassigned. Unlike | 
| 5522 |  |  |  |  |  |  | #	bash, this assignment takes place on positional parameters. If | 
| 5523 |  |  |  |  |  |  | #	done on an attribute or environment variable, it causes that | 
| 5524 |  |  |  |  |  |  | #	attribute or environment variable to be set to the given value. | 
| 5525 |  |  |  |  |  |  | # | 
| 5526 |  |  |  |  |  |  | #	${parameter:?word} causes the parse to fail with the error | 
| 5527 |  |  |  |  |  |  | #	'word' if the parameter is undefined. | 
| 5528 |  |  |  |  |  |  | # | 
| 5529 |  |  |  |  |  |  | #	${parameter:+word} causes the value of the given word to be used | 
| 5530 |  |  |  |  |  |  | #	if the parameter is defined, otherwise '' is used. | 
| 5531 |  |  |  |  |  |  | # | 
| 5532 |  |  |  |  |  |  | #	${parameter:offset} and ${parameter:offset:length} take | 
| 5533 |  |  |  |  |  |  | #	substrings of the parameter value. The offset and length must be | 
| 5534 |  |  |  |  |  |  | #	numeric. | 
| 5535 |  |  |  |  |  |  |  | 
| 5536 |  |  |  |  |  |  | { | 
| 5537 |  |  |  |  |  |  |  | 
| 5538 |  |  |  |  |  |  | # Special variables. | 
| 5539 |  |  |  |  |  |  | # Calling sequence: $special{$name}->(\@args, $relquote) | 
| 5540 |  |  |  |  |  |  | my %special = ( | 
| 5541 |  |  |  |  |  |  | '0' => sub { return $0 }, | 
| 5542 |  |  |  |  |  |  | '#' => sub { return scalar @{ $_[0] } }, | 
| 5543 |  |  |  |  |  |  | ##	'*' => sub { return join ' ', @{ $_[0] } }, | 
| 5544 |  |  |  |  |  |  | ##	'@' => sub { return $_[1] ? join( ' ', @{ $_[0] } ) : $_[0] }, | 
| 5545 |  |  |  |  |  |  | '*' => sub { return $_[1] ? join( ' ', @{ $_[0] } ) : $_[0] }, | 
| 5546 |  |  |  |  |  |  | '@' => sub { return $_[0] }, | 
| 5547 |  |  |  |  |  |  | '$' => sub { return $$ }, | 
| 5548 |  |  |  |  |  |  | '_' => sub { return $^X }, | 
| 5549 |  |  |  |  |  |  | ); | 
| 5550 |  |  |  |  |  |  |  | 
| 5551 |  |  |  |  |  |  | my %case_ctl = ( | 
| 5552 |  |  |  |  |  |  | E	=> sub { delete $_[0]->{_case_mod} }, | 
| 5553 |  |  |  |  |  |  | F	=> sub { $_[0]->{_case_mod}{case} = sub { fold_case( $_[1] ) } }, | 
| 5554 |  |  |  |  |  |  | L	=> sub { $_[0]->{_case_mod}{case} = sub { lc $_[1] } }, | 
| 5555 |  |  |  |  |  |  | U	=> sub { $_[0]->{_case_mod}{case} = sub { uc $_[1] } }, | 
| 5556 |  |  |  |  |  |  | l	=> sub { $_[0]->{_case_mod}{single} = sub { lcfirst $_[1] } }, | 
| 5557 |  |  |  |  |  |  | u	=> sub { $_[0]->{_case_mod}{single} = sub { ucfirst $_[1] } }, | 
| 5558 |  |  |  |  |  |  | ); | 
| 5559 |  |  |  |  |  |  |  | 
| 5560 |  |  |  |  |  |  | # Leading punctuation that is equivalent to a method. | 
| 5561 |  |  |  |  |  |  | my %command_equivalent = ( | 
| 5562 |  |  |  |  |  |  | '.'	=> 'source', | 
| 5563 |  |  |  |  |  |  | '!' => 'system', | 
| 5564 |  |  |  |  |  |  | ); | 
| 5565 |  |  |  |  |  |  | my $command_equiv_re = do { | 
| 5566 |  |  |  |  |  |  | my $keys = join '', sort keys %command_equivalent; | 
| 5567 |  |  |  |  |  |  | qr{ [$keys] }smx; | 
| 5568 |  |  |  |  |  |  | }; | 
| 5569 |  |  |  |  |  |  |  | 
| 5570 |  |  |  |  |  |  | my %escape = ( | 
| 5571 |  |  |  |  |  |  | t	=> "\t", | 
| 5572 |  |  |  |  |  |  | n	=> "\n", | 
| 5573 |  |  |  |  |  |  | r	=> "\r", | 
| 5574 |  |  |  |  |  |  | f	=> "\f", | 
| 5575 |  |  |  |  |  |  | b	=> "\b", | 
| 5576 |  |  |  |  |  |  | a	=> "\a", | 
| 5577 |  |  |  |  |  |  | e	=> "\e", | 
| 5578 |  |  |  |  |  |  | ); | 
| 5579 |  |  |  |  |  |  |  | 
| 5580 |  |  |  |  |  |  | sub __tokenize { | 
| 5581 | 381 |  |  | 381 |  | 79492 | my ($self, @parms) = @_; | 
| 5582 | 381 |  |  |  |  | 1171 | local $self->{_case_mod} = undef; | 
| 5583 | 381 | 100 |  |  |  | 1163 | my $opt = HASH_REF eq ref $parms[0] ? shift @parms : {}; | 
| 5584 | 381 |  |  |  |  | 727 | my $in = $opt->{in}; | 
| 5585 | 381 |  |  |  |  | 685 | my $buffer = shift @parms; | 
| 5586 | 381 | 100 |  |  |  | 1445 | $buffer =~ m/ \n \z /smx or $buffer .= "\n"; | 
| 5587 | 381 |  | 100 |  |  | 964 | my $args = shift @parms || []; | 
| 5588 | 381 |  |  |  |  | 878 | my @rslt = ( {} ); | 
| 5589 | 381 |  |  |  |  | 621 | my $absquote;	# True if inside '' | 
| 5590 |  |  |  |  |  |  | my $relquote;	# True if inside "" (and not in '') | 
| 5591 | 381 |  |  |  |  | 720 | my $len = length $buffer; | 
| 5592 | 381 |  |  |  |  | 636 | my $inx = 0; | 
| 5593 |  |  |  |  |  |  |  | 
| 5594 |  |  |  |  |  |  | # Because I'm not smart enough to do all this with a regular | 
| 5595 |  |  |  |  |  |  | # expression, I take the brute force approach and iterate | 
| 5596 |  |  |  |  |  |  | # through the buffer to be tokenized. It's a 'while' rather than | 
| 5597 |  |  |  |  |  |  | # a 'for' or 'foreach' because that way I get to muck around | 
| 5598 |  |  |  |  |  |  | # with the current position inside the loop. | 
| 5599 |  |  |  |  |  |  |  | 
| 5600 | 381 |  |  |  |  | 873 | while ($inx < $len) { | 
| 5601 | 6312 |  |  |  |  | 10599 | my $char = substr $buffer, $inx++, 1; | 
| 5602 |  |  |  |  |  |  |  | 
| 5603 |  |  |  |  |  |  | # If we're inside single quotes, the only escapable | 
| 5604 |  |  |  |  |  |  | # characters are single quote and back slash, and all | 
| 5605 |  |  |  |  |  |  | # characters until the next unescaped single quote go into | 
| 5606 |  |  |  |  |  |  | # the current token | 
| 5607 |  |  |  |  |  |  |  | 
| 5608 | 6312 | 100 | 66 |  |  | 37420 | if ( $absquote ) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 5609 | 621 | 50 |  |  |  | 1268 | if ( $char eq '\\' ) { | 
|  |  | 100 |  |  |  |  |  | 
| 5610 | 0 | 0 |  |  |  | 0 | if ( (my $next = substr $buffer, $inx, 1) =~ | 
| 5611 |  |  |  |  |  |  | m/ ['\\] /smx ) { | 
| 5612 | 0 |  |  |  |  | 0 | $inx++; | 
| 5613 | 0 |  |  |  |  | 0 | $rslt[-1]{token} .= $next; | 
| 5614 |  |  |  |  |  |  | } else { | 
| 5615 | 0 |  |  |  |  | 0 | $rslt[-1]{token} .= $char; | 
| 5616 |  |  |  |  |  |  | } | 
| 5617 |  |  |  |  |  |  | } elsif ( $char eq q{'} ) { | 
| 5618 | 34 |  |  |  |  | 106 | $absquote = undef; | 
| 5619 |  |  |  |  |  |  | } else { | 
| 5620 | 587 |  |  |  |  | 827 | $rslt[-1]{token} .= $char; | 
| 5621 | 587 | 100 |  |  |  | 1030 | if ( $inx >= $len ) { | 
| 5622 | 2 |  |  |  |  | 39 | $buffer .= $self->_read_continuation( $in, | 
| 5623 |  |  |  |  |  |  | 'Unclosed single quote' ); | 
| 5624 | 1 |  |  |  |  | 3 | $len = length $buffer; | 
| 5625 |  |  |  |  |  |  | } | 
| 5626 |  |  |  |  |  |  | } | 
| 5627 |  |  |  |  |  |  |  | 
| 5628 |  |  |  |  |  |  | # If we have a backslash, it escapes the next character, | 
| 5629 |  |  |  |  |  |  | # which goes on the current token no matter what it is. | 
| 5630 |  |  |  |  |  |  |  | 
| 5631 |  |  |  |  |  |  | } elsif ( $char eq '\\' ) { | 
| 5632 | 10 |  |  |  |  | 35 | my $next = substr $buffer, $inx++, 1; | 
| 5633 | 10 | 100 |  |  |  | 52 | if ( $inx >= $len ) {	# At end of line | 
|  |  | 100 |  |  |  |  |  | 
| 5634 | 2 | 50 |  |  |  | 29 | if ( $relquote ) {	# Inside "" | 
| 5635 | 0 |  |  |  |  | 0 | $buffer .= $self->_read_continuation( $in, | 
| 5636 |  |  |  |  |  |  | 'Unclosed double quote' ); | 
| 5637 |  |  |  |  |  |  | } else {		# Between tokens | 
| 5638 | 2 |  |  |  |  | 31 | $buffer .= $self->_read_continuation( $in, | 
| 5639 |  |  |  |  |  |  | 'Dangling continuation' ); | 
| 5640 | 2 | 50 |  |  |  | 24 | $opt->{single} or push @rslt, {};	# New token | 
| 5641 |  |  |  |  |  |  | } | 
| 5642 | 2 |  |  |  |  | 12 | $len = length $buffer; | 
| 5643 |  |  |  |  |  |  | } elsif ( $relquote ) { | 
| 5644 | 7 | 100 |  |  |  | 34 | if ( my $code = $case_ctl{$next} ) { | 
| 5645 | 6 |  |  |  |  | 18 | $code->( $self ); | 
| 5646 |  |  |  |  |  |  | } else { | 
| 5647 | 1 |  | 33 |  |  | 5 | $rslt[-1]{token} .= $escape{$next} || $next; | 
| 5648 |  |  |  |  |  |  | } | 
| 5649 |  |  |  |  |  |  | } else { | 
| 5650 | 1 |  |  |  |  | 2 | $rslt[-1]{token} .= $next; | 
| 5651 |  |  |  |  |  |  | } | 
| 5652 |  |  |  |  |  |  |  | 
| 5653 |  |  |  |  |  |  | # If we have a single quote and we're not inside double | 
| 5654 |  |  |  |  |  |  | # quotes, we go into absolute quote mode. We also append an | 
| 5655 |  |  |  |  |  |  | # empty string to the current token to force its value to be | 
| 5656 |  |  |  |  |  |  | # defined; otherwise empty quotes do not generate tokens. | 
| 5657 |  |  |  |  |  |  |  | 
| 5658 |  |  |  |  |  |  | } elsif ($char eq q{'} && !$relquote) { | 
| 5659 | 35 |  |  |  |  | 115 | $rslt[-1]{token} .= '';	# Empty string, to force defined. | 
| 5660 | 35 |  |  |  |  | 73 | $absquote++; | 
| 5661 |  |  |  |  |  |  |  | 
| 5662 |  |  |  |  |  |  | # If we have a double quote, we toggle relative quote mode. | 
| 5663 |  |  |  |  |  |  | # We also append an empty string to the current tokens for | 
| 5664 |  |  |  |  |  |  | # the reasons discussed above. | 
| 5665 |  |  |  |  |  |  |  | 
| 5666 |  |  |  |  |  |  | } elsif ($char eq '"') { | 
| 5667 | 44 |  |  |  |  | 99 | $rslt[-1]{token} .= '';	# Empty string, to force defined. | 
| 5668 |  |  |  |  |  |  | ( $relquote = !$relquote ) | 
| 5669 | 44 | 100 |  |  |  | 144 | or delete $self->{_case_mod}; | 
| 5670 |  |  |  |  |  |  |  | 
| 5671 |  |  |  |  |  |  | # If we have a whitespace character and we're not inside | 
| 5672 |  |  |  |  |  |  | # quotes and not in single-token mode, we start a new token. | 
| 5673 |  |  |  |  |  |  | # It is possible that we generate redundant tokens this way, | 
| 5674 |  |  |  |  |  |  | # but the unused ones are eliminated later. | 
| 5675 |  |  |  |  |  |  |  | 
| 5676 |  |  |  |  |  |  | } elsif ($char =~ m/ \s /smx && !$relquote && !$opt->{single}) { | 
| 5677 | 937 |  |  |  |  | 1863 | push @rslt, {}; | 
| 5678 |  |  |  |  |  |  |  | 
| 5679 |  |  |  |  |  |  | # If we have a dollar sign, it introduces parameter | 
| 5680 |  |  |  |  |  |  | # substitution, a non trivial endeavor. | 
| 5681 |  |  |  |  |  |  |  | 
| 5682 |  |  |  |  |  |  | } elsif ( $char eq '$' && $inx < $len ) { | 
| 5683 | 72 |  |  |  |  | 154 | my $name = substr $buffer, $inx++, 1; | 
| 5684 | 72 |  |  |  |  | 103 | my $brkt; | 
| 5685 |  |  |  |  |  |  |  | 
| 5686 |  |  |  |  |  |  | # Names beginning with brackets are special. We note the | 
| 5687 |  |  |  |  |  |  | # fact and scan for the matching close bracket, throwing | 
| 5688 |  |  |  |  |  |  | # an exception if we do not have one. | 
| 5689 |  |  |  |  |  |  |  | 
| 5690 | 72 | 100 | 66 |  |  | 381 | if ($name eq '{' && $inx < $len) { | 
|  |  | 100 |  |  |  |  |  | 
| 5691 | 34 |  |  |  |  | 59 | $brkt = 1; | 
| 5692 | 34 |  |  |  |  | 48 | $name = ''; | 
| 5693 | 34 |  |  |  |  | 87 | my $nest = 1; | 
| 5694 | 34 |  |  |  |  | 77 | while ($inx < $len) { | 
| 5695 | 369 |  |  |  |  | 500 | $char = substr $buffer, $inx++, 1; | 
| 5696 | 369 | 50 |  |  |  | 753 | if ($char eq '{') { | 
|  |  | 100 |  |  |  |  |  | 
| 5697 | 0 |  |  |  |  | 0 | $nest++; | 
| 5698 |  |  |  |  |  |  | } elsif ($char eq '}') { | 
| 5699 | 33 | 50 |  |  |  | 95 | --$nest or last; | 
| 5700 |  |  |  |  |  |  | } | 
| 5701 | 336 |  |  |  |  | 572 | $name .= $char; | 
| 5702 |  |  |  |  |  |  | } | 
| 5703 | 34 | 100 |  |  |  | 85 | $char eq '}' | 
| 5704 |  |  |  |  |  |  | or $self->wail('Missing right curly bracket'); | 
| 5705 |  |  |  |  |  |  |  | 
| 5706 |  |  |  |  |  |  | # If the name begins with an alpha or an underscore, we | 
| 5707 |  |  |  |  |  |  | # simply append any word ('\w') characters to it. If it | 
| 5708 |  |  |  |  |  |  | # the word characters are immediately followed by a dot | 
| 5709 |  |  |  |  |  |  | # and more word characters we grab them too, and advance | 
| 5710 |  |  |  |  |  |  | # the current location past whatever we grabbed. The dot | 
| 5711 |  |  |  |  |  |  | # syntax is in aid of accessing attributes of | 
| 5712 |  |  |  |  |  |  | # attributes (e.g. $formatter.time_format) | 
| 5713 |  |  |  |  |  |  |  | 
| 5714 |  |  |  |  |  |  | } elsif ( $name =~ m/ \A [[:alpha:]_] \z /smx ) { | 
| 5715 | 21 |  |  |  |  | 82 | pos( $buffer ) = $inx; | 
| 5716 | 21 | 50 |  |  |  | 438 | if ( $buffer =~ m/ \G ( \w* (?: [.] \w+ )? ) /smxgc ) { | 
| 5717 | 21 |  |  |  |  | 66 | $name .= $1; | 
| 5718 | 21 |  |  |  |  | 47 | $inx += length $1; | 
| 5719 |  |  |  |  |  |  | } | 
| 5720 |  |  |  |  |  |  | } | 
| 5721 |  |  |  |  |  |  |  | 
| 5722 |  |  |  |  |  |  | # Only bracketed names can be indirected, and then only | 
| 5723 |  |  |  |  |  |  | # if the first character is a bang. | 
| 5724 |  |  |  |  |  |  |  | 
| 5725 | 71 |  |  |  |  | 130 | my ($indirect, $value); | 
| 5726 | 71 | 100 |  |  |  | 198 | $brkt and $indirect = $name =~ s/ \A ! //smx; | 
| 5727 |  |  |  |  |  |  |  | 
| 5728 |  |  |  |  |  |  | # If we find a colon and/or one of the other cabbalistic | 
| 5729 |  |  |  |  |  |  | # characters, we need to do some default processing. | 
| 5730 |  |  |  |  |  |  |  | 
| 5731 | 71 | 100 |  |  |  | 378 | if ($name =~ m/ (.*?) ( [:]? [\-\+\=\?] | [:] ) (.*) /smx) { | 
| 5732 | 28 |  |  |  |  | 133 | my ($name, $flag, $rest) = ($1, $2, $3); | 
| 5733 |  |  |  |  |  |  |  | 
| 5734 |  |  |  |  |  |  | # First we do indirection if that was required. | 
| 5735 |  |  |  |  |  |  |  | 
| 5736 | 28 | 50 |  |  |  | 81 | $indirect | 
| 5737 |  |  |  |  |  |  | and $name = $self->_tokenize_var( | 
| 5738 |  |  |  |  |  |  | $name, $args, $relquote, $indirect); | 
| 5739 |  |  |  |  |  |  |  | 
| 5740 |  |  |  |  |  |  | # Next we find out whether we have an honest-to-God | 
| 5741 |  |  |  |  |  |  | # colon, since that might specify substring | 
| 5742 |  |  |  |  |  |  | # processing. | 
| 5743 |  |  |  |  |  |  |  | 
| 5744 |  |  |  |  |  |  | ##		    my $colon = $flag =~ s/ \A : //smx ? ':' : ''; | 
| 5745 | 28 |  |  |  |  | 111 | $flag =~ s/ \A : //smx; | 
| 5746 |  |  |  |  |  |  |  | 
| 5747 |  |  |  |  |  |  | # We run the stuff after the first cabbalistic | 
| 5748 |  |  |  |  |  |  | # character through the tokenizer, since further | 
| 5749 |  |  |  |  |  |  | # expansion is possible here. | 
| 5750 |  |  |  |  |  |  |  | 
| 5751 | 28 |  |  |  |  | 174 | my $mod = __tokenize( | 
| 5752 |  |  |  |  |  |  | $self, | 
| 5753 |  |  |  |  |  |  | { single => 1, noredirect => 1, in => $in }, | 
| 5754 |  |  |  |  |  |  | $rest, $args); | 
| 5755 | 28 |  |  |  |  | 77 | chomp $mod;	# Don't want trailing \n here. | 
| 5756 |  |  |  |  |  |  |  | 
| 5757 |  |  |  |  |  |  | # At long last we get the actual value of the | 
| 5758 |  |  |  |  |  |  | # variable. This will be either undef, a scalar, or | 
| 5759 |  |  |  |  |  |  | # a list reference. | 
| 5760 |  |  |  |  |  |  |  | 
| 5761 | 28 |  |  |  |  | 68 | $value = $self->_tokenize_var( | 
| 5762 |  |  |  |  |  |  | $name, $args, $relquote); | 
| 5763 |  |  |  |  |  |  |  | 
| 5764 |  |  |  |  |  |  | # The value is logically defined if it is a scalar | 
| 5765 |  |  |  |  |  |  | # and not undef, or if it is an array reference and | 
| 5766 |  |  |  |  |  |  | # the array is not empty. | 
| 5767 |  |  |  |  |  |  |  | 
| 5768 | 28 | 100 |  |  |  | 83 | my $defined = ref $value ? @$value : defined $value; | 
| 5769 |  |  |  |  |  |  |  | 
| 5770 |  |  |  |  |  |  | # The '+' cabbalistic sign replaces the value of the | 
| 5771 |  |  |  |  |  |  | # variable if it is logically defined. | 
| 5772 |  |  |  |  |  |  |  | 
| 5773 | 28 | 100 |  |  |  | 116 | if ($flag eq '+') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 5774 | 4 | 100 |  |  |  | 26 | $value = $defined ? $mod : ''; | 
| 5775 |  |  |  |  |  |  |  | 
| 5776 |  |  |  |  |  |  | # If the variable is defined, only substring | 
| 5777 |  |  |  |  |  |  | # processing is possible. This actually is | 
| 5778 |  |  |  |  |  |  | # implemented as slice processing if the value is an | 
| 5779 |  |  |  |  |  |  | # array reference. | 
| 5780 |  |  |  |  |  |  |  | 
| 5781 |  |  |  |  |  |  | } elsif ($defined) { | 
| 5782 | 16 | 100 |  |  |  | 47 | if ($flag eq '') { | 
| 5783 | 10 |  |  |  |  | 40 | my @pos = split ':', $mod, 2; | 
| 5784 | 10 |  |  |  |  | 20 | foreach ( @pos ) { | 
| 5785 | 18 |  |  |  |  | 47 | s/ \A \s+ //smx; | 
| 5786 |  |  |  |  |  |  | } | 
| 5787 | 10 | 50 |  |  |  | 27 | @pos > 2 | 
| 5788 |  |  |  |  |  |  | and $self->wail( | 
| 5789 |  |  |  |  |  |  | 'Substring expansion has extra arguments' ); | 
| 5790 | 10 |  |  |  |  | 23 | foreach ( @pos ) { | 
| 5791 | 18 | 50 |  |  |  | 78 | m/ \A [-+]? [0-9]+ \z /smx | 
| 5792 |  |  |  |  |  |  | or $self->wail( | 
| 5793 |  |  |  |  |  |  | 'Substring expansion argument non-numeric' | 
| 5794 |  |  |  |  |  |  | ); | 
| 5795 |  |  |  |  |  |  | } | 
| 5796 | 10 | 100 |  |  |  | 25 | if (ref $value) { | 
| 5797 | 4 | 50 |  |  |  | 10 | if (@pos > 1) { | 
| 5798 | 4 |  |  |  |  | 13 | $pos[1] += $pos[0] - 1; | 
| 5799 |  |  |  |  |  |  | } else { | 
| 5800 | 0 |  |  |  |  | 0 | $pos[1] = $#$args; | 
| 5801 |  |  |  |  |  |  | } | 
| 5802 | 4 | 100 |  |  |  | 14 | $pos[1] > $#$value and $pos[1] = $#$value; | 
| 5803 | 4 |  |  |  |  | 20 | $value = [@$value[$pos[0] .. $pos[1]]]; | 
| 5804 |  |  |  |  |  |  | } else { | 
| 5805 |  |  |  |  |  |  | # We want to disable warnings if we slop | 
| 5806 |  |  |  |  |  |  | # outside the string. | 
| 5807 | 20 |  |  | 20 |  | 211 | no warnings qw{substr}; | 
|  | 20 |  |  |  |  | 46 |  | 
|  | 20 |  |  |  |  | 54666 |  | 
| 5808 | 6 | 100 |  |  |  | 27 | $value = @pos == 1 ? substr $value, $pos[0] : | 
| 5809 |  |  |  |  |  |  | substr $value, $pos[0], $pos[1]; | 
| 5810 |  |  |  |  |  |  | } | 
| 5811 |  |  |  |  |  |  | } | 
| 5812 |  |  |  |  |  |  |  | 
| 5813 |  |  |  |  |  |  | # If the cabbalistic sign is '-', we supply the | 
| 5814 |  |  |  |  |  |  | # remainder of the specification as the default. | 
| 5815 |  |  |  |  |  |  |  | 
| 5816 |  |  |  |  |  |  | } elsif ($flag eq '-') { | 
| 5817 | 2 |  |  |  |  | 7 | $value = $mod; | 
| 5818 |  |  |  |  |  |  |  | 
| 5819 |  |  |  |  |  |  | # If the cabbalistic sign is '=', we supply the | 
| 5820 |  |  |  |  |  |  | # remainder of the specification as the default. We | 
| 5821 |  |  |  |  |  |  | # also set the variable to the value, for future | 
| 5822 |  |  |  |  |  |  | # use. Note that special variables may not be set, | 
| 5823 |  |  |  |  |  |  | # and result in an exception. | 
| 5824 |  |  |  |  |  |  |  | 
| 5825 |  |  |  |  |  |  | } elsif ($flag eq '=') { | 
| 5826 | 3 |  |  |  |  | 11 | $value = $mod; | 
| 5827 | 3 | 50 | 33 |  |  | 87 | if ( $special{$name} || $name !~ m/ \D /smx ) { | 
|  |  | 50 |  |  |  |  |  | 
| 5828 | 0 |  |  |  |  | 0 | $self->wail("Cannot assign to \$$name"); | 
| 5829 |  |  |  |  |  |  | ##			} elsif ($name !~ m/\D/) { | 
| 5830 |  |  |  |  |  |  | ##			    $args->[$name - 1] = $value; | 
| 5831 |  |  |  |  |  |  | } elsif (exists $mutator{$name}) { | 
| 5832 | 0 |  |  |  |  | 0 | $self->set($name => $value); | 
| 5833 |  |  |  |  |  |  | } else { | 
| 5834 | 3 |  |  |  |  | 33 | $self->{frame}[-1]{define}{$name} = $value; | 
| 5835 |  |  |  |  |  |  | } | 
| 5836 |  |  |  |  |  |  |  | 
| 5837 |  |  |  |  |  |  | # If the cabbalistic sign is '?', we throw an | 
| 5838 |  |  |  |  |  |  | # exception with the remainder of the specification | 
| 5839 |  |  |  |  |  |  | # as the text. | 
| 5840 |  |  |  |  |  |  |  | 
| 5841 |  |  |  |  |  |  | } elsif ($flag eq '?') { | 
| 5842 | 2 |  |  |  |  | 20 | $self->wail($mod); | 
| 5843 |  |  |  |  |  |  |  | 
| 5844 |  |  |  |  |  |  | # If there is no cabbalistic sign at all, we fell | 
| 5845 |  |  |  |  |  |  | # through here trying to do substring expansion on | 
| 5846 |  |  |  |  |  |  | # an undefined variable. Since Bash allows this, we | 
| 5847 |  |  |  |  |  |  | # will to, though with misgivings. | 
| 5848 |  |  |  |  |  |  |  | 
| 5849 |  |  |  |  |  |  | } elsif ( $flag eq '' ) { | 
| 5850 | 1 |  |  |  |  | 4 | $value = ''; | 
| 5851 |  |  |  |  |  |  |  | 
| 5852 |  |  |  |  |  |  | # Given the way the parser works, the above should | 
| 5853 |  |  |  |  |  |  | # have exhausted all possibilities. But being a | 
| 5854 |  |  |  |  |  |  | # cautious programmer ... | 
| 5855 |  |  |  |  |  |  |  | 
| 5856 |  |  |  |  |  |  | } else { | 
| 5857 | 0 |  |  |  |  | 0 | $self->weep( | 
| 5858 |  |  |  |  |  |  | "\$flag = '$flag'. This should not happen" | 
| 5859 |  |  |  |  |  |  | ); | 
| 5860 |  |  |  |  |  |  | } | 
| 5861 |  |  |  |  |  |  |  | 
| 5862 |  |  |  |  |  |  | # Without any cabbalistic signs, variable expansion is | 
| 5863 |  |  |  |  |  |  | # easy. We perform the indirection if needed, and then | 
| 5864 |  |  |  |  |  |  | # grab the value of the variable, which still can be | 
| 5865 |  |  |  |  |  |  | # undef, a scalar, or an array reference. | 
| 5866 |  |  |  |  |  |  |  | 
| 5867 |  |  |  |  |  |  | } else { | 
| 5868 | 43 | 100 |  |  |  | 115 | $indirect | 
| 5869 |  |  |  |  |  |  | and $name = $self->_tokenize_var( | 
| 5870 |  |  |  |  |  |  | $name, $args, $relquote, $indirect); | 
| 5871 | 43 |  |  |  |  | 127 | $value = $self->_tokenize_var( | 
| 5872 |  |  |  |  |  |  | $name, $args, $relquote); | 
| 5873 |  |  |  |  |  |  | } | 
| 5874 |  |  |  |  |  |  |  | 
| 5875 |  |  |  |  |  |  | # For simplicity in what follows, make the value into an | 
| 5876 |  |  |  |  |  |  | # array reference. | 
| 5877 | 69 | 100 |  |  |  | 258 | ref $value | 
|  |  | 100 |  |  |  |  |  | 
| 5878 |  |  |  |  |  |  | or $value = defined $value ? [ $value ] : []; | 
| 5879 |  |  |  |  |  |  |  | 
| 5880 |  |  |  |  |  |  | # If we are inside quotes | 
| 5881 | 69 | 100 |  |  |  | 145 | if ( $relquote ) { | 
| 5882 |  |  |  |  |  |  | # do case modification | 
| 5883 |  |  |  |  |  |  | # NOTE that the argument list is modified in-place. | 
| 5884 | 12 |  |  |  |  | 23 | $self->_case_mod( @{ $value } ); | 
|  | 12 |  |  |  |  | 37 |  | 
| 5885 |  |  |  |  |  |  | } else { | 
| 5886 |  |  |  |  |  |  | # otherwise do word splitting | 
| 5887 | 57 |  |  |  |  | 86 | $value = [ map { split qr{ \s+ }smx } @{ $value } ]; | 
|  | 71 |  |  |  |  | 466 |  | 
|  | 57 |  |  |  |  | 134 |  | 
| 5888 |  |  |  |  |  |  | } | 
| 5889 |  |  |  |  |  |  |  | 
| 5890 |  |  |  |  |  |  | # If we have a value, append each element to the current | 
| 5891 |  |  |  |  |  |  | # token, and then create a new token for the next | 
| 5892 |  |  |  |  |  |  | # element. The last element's empty token gets | 
| 5893 |  |  |  |  |  |  | # discarded, since we may need to append more data to | 
| 5894 |  |  |  |  |  |  | # the last element (e.g.  "$@ foo"). | 
| 5895 | 69 | 100 |  |  |  | 159 | if ( @{ $value } ) { | 
|  | 69 |  |  |  |  | 178 |  | 
| 5896 | 58 |  |  |  |  | 105 | foreach ( @$value ) { | 
| 5897 | 86 |  |  |  |  | 219 | $rslt[-1]{token} .= $_; | 
| 5898 | 86 |  |  |  |  | 186 | push @rslt, {}; | 
| 5899 |  |  |  |  |  |  | } | 
| 5900 | 58 |  |  |  |  | 107 | pop @rslt; | 
| 5901 |  |  |  |  |  |  | } | 
| 5902 |  |  |  |  |  |  |  | 
| 5903 |  |  |  |  |  |  | # Here ends the variable expansion code. | 
| 5904 |  |  |  |  |  |  |  | 
| 5905 |  |  |  |  |  |  | # If the character is an angle bracket or a pipe, we have a | 
| 5906 |  |  |  |  |  |  | # redirect specification. This always starts a new token. We | 
| 5907 |  |  |  |  |  |  | # flag the token as a redirect, stuff all matching | 
| 5908 |  |  |  |  |  |  | # characters into the mode (throwing an exception if there | 
| 5909 |  |  |  |  |  |  | # are too many), consume any trailing spaces, and set the | 
| 5910 |  |  |  |  |  |  | # token value to the empty string to prevent executing this | 
| 5911 |  |  |  |  |  |  | # code again when we hit the first character of the file | 
| 5912 |  |  |  |  |  |  | # name. Note that redirect tokens always get tilde | 
| 5913 |  |  |  |  |  |  | # expansion. | 
| 5914 |  |  |  |  |  |  |  | 
| 5915 |  |  |  |  |  |  | } elsif ( $char =~ m/ [<>|] /smx ) { | 
| 5916 | 6 | 100 |  |  |  | 67 | push @rslt, { | 
|  |  | 50 |  |  |  |  |  | 
| 5917 |  |  |  |  |  |  | redirect => 1, | 
| 5918 |  |  |  |  |  |  | type => ($char eq '<' ? '<' : '>'), | 
| 5919 |  |  |  |  |  |  | mode => ($char eq '|' ? '|-' : $char), | 
| 5920 |  |  |  |  |  |  | expand => ($char ne '|') | 
| 5921 |  |  |  |  |  |  | }; | 
| 5922 | 6 |  |  |  |  | 19 | while ($inx < $len) { | 
| 5923 | 11 |  |  |  |  | 25 | my $next = substr $buffer, $inx++, 1; | 
| 5924 | 11 | 50 |  |  |  | 41 | $next =~ m/ \s /smx and next; | 
| 5925 | 11 | 100 |  |  |  | 29 | if ($next eq $char) { | 
| 5926 | 6 |  |  |  |  | 14 | $rslt[-1]{mode} .= $next; | 
| 5927 | 6 | 100 |  |  |  | 32 | length $rslt[-1]{mode} > 2 | 
| 5928 |  |  |  |  |  |  | and $self->wail( | 
| 5929 |  |  |  |  |  |  | "Syntax error near $rslt[-1]{mode}"); | 
| 5930 |  |  |  |  |  |  | } else { | 
| 5931 | 5 |  |  |  |  | 8 | --$inx; | 
| 5932 | 5 |  |  |  |  | 16 | $rslt[-1]{token} = ''; | 
| 5933 | 5 |  |  |  |  | 13 | last; | 
| 5934 |  |  |  |  |  |  | } | 
| 5935 |  |  |  |  |  |  | } | 
| 5936 | 5 | 100 |  |  |  | 21 | if ( '<<' eq $rslt[-1]{mode} ) {	# Heredoc | 
| 5937 | 4 |  |  |  |  | 8 | delete $rslt[-1]{redirect}; | 
| 5938 | 4 |  |  |  |  | 7 | delete $rslt[-1]{type}; | 
| 5939 | 4 |  |  |  |  | 25 | delete $rslt[-1]{mode}; | 
| 5940 | 4 |  |  |  |  | 11 | my $quote = ''; | 
| 5941 | 4 |  |  |  |  | 12 | while ( $inx < $len ) { | 
| 5942 | 62 |  |  |  |  | 82 | my $next = substr $buffer, $inx++, 1; | 
| 5943 | 62 | 100 |  |  |  | 123 | if ( $next =~ m/ \s /smx ) { | 
| 5944 | 2 | 50 |  |  |  | 8 | $quote or last; | 
| 5945 | 0 |  |  |  |  | 0 | $rslt[-1]{token} .= $next; | 
| 5946 |  |  |  |  |  |  | } else { | 
| 5947 |  |  |  |  |  |  | '' eq $rslt[-1]{token} | 
| 5948 |  |  |  |  |  |  | and $next =~ m/ ['"] /smx | 
| 5949 |  |  |  |  |  |  | and $quote = $next | 
| 5950 | 60 | 100 | 100 |  |  | 150 | or $rslt[-1]{token} .= $next; | 
|  |  |  | 66 |  |  |  |  | 
| 5951 |  |  |  |  |  |  | $quote | 
| 5952 |  |  |  |  |  |  | and $next eq $quote | 
| 5953 | 60 | 100 | 100 |  |  | 181 | and $rslt[-1]{token} ne '' | 
|  |  |  | 100 |  |  |  |  | 
| 5954 |  |  |  |  |  |  | and last; | 
| 5955 |  |  |  |  |  |  | } | 
| 5956 |  |  |  |  |  |  | } | 
| 5957 | 4 | 100 |  |  |  | 15 | $quote and $rslt[-1]{token} =~ s/ . \z //sxm; | 
| 5958 | 4 |  |  |  |  | 15 | my $terminator = $rslt[-1]{token}; | 
| 5959 | 4 |  |  |  |  | 11 | my $look_for = $terminator . "\n"; | 
| 5960 | 4 |  |  |  |  | 9 | $rslt[-1]{token} = ''; | 
| 5961 | 4 |  |  |  |  | 8 | $rslt[-1]{expand} = $quote ne q<'>; | 
| 5962 | 4 |  |  |  |  | 6 | while ( 1 ) { | 
| 5963 | 9 |  |  |  |  | 37 | my $buffer = $self->_read_continuation( $in, | 
| 5964 |  |  |  |  |  |  | "Here doc terminator $terminator not found" ); | 
| 5965 | 9 | 100 |  |  |  | 29 | $buffer eq $look_for and last; | 
| 5966 | 5 |  |  |  |  | 12 | $rslt[-1]{token} .= $buffer; | 
| 5967 |  |  |  |  |  |  | } | 
| 5968 | 4 | 100 |  |  |  | 12 | if ( $quote ne q<'> ) { | 
| 5969 |  |  |  |  |  |  | $rslt[-1]{token} = __tokenize( | 
| 5970 |  |  |  |  |  |  | $self, | 
| 5971 |  |  |  |  |  |  | { single => 1, noredirect => 1, in => $in }, | 
| 5972 | 3 |  |  |  |  | 46 | $rslt[-1]{token}, $args | 
| 5973 |  |  |  |  |  |  | ); | 
| 5974 |  |  |  |  |  |  | } | 
| 5975 | 4 |  |  |  |  | 16 | push @rslt, {};	# New token | 
| 5976 |  |  |  |  |  |  | } | 
| 5977 |  |  |  |  |  |  |  | 
| 5978 |  |  |  |  |  |  | # If the token already exists at this point, the current | 
| 5979 |  |  |  |  |  |  | # character, whatever it is, is simply appended to it. | 
| 5980 |  |  |  |  |  |  |  | 
| 5981 |  |  |  |  |  |  | } elsif (exists $rslt[-1]{token} || $relquote) { | 
| 5982 |  |  |  |  |  |  | # do case modification | 
| 5983 |  |  |  |  |  |  | # NOTE that the argument list is modified in-place. | 
| 5984 | 3744 |  |  |  |  | 8633 | $self->_case_mod( $char ); | 
| 5985 | 3744 |  |  |  |  | 5704 | $rslt[-1]{token} .= $char; | 
| 5986 |  |  |  |  |  |  |  | 
| 5987 |  |  |  |  |  |  | # If the character is a tilde, we flag the token for tilde | 
| 5988 |  |  |  |  |  |  | # expansion. | 
| 5989 |  |  |  |  |  |  |  | 
| 5990 |  |  |  |  |  |  | } elsif ($char eq '~') { | 
| 5991 | 12 |  |  |  |  | 54 | $rslt[-1]{tilde}++; | 
| 5992 | 12 |  |  |  |  | 53 | $rslt[-1]{token} .= $char; | 
| 5993 |  |  |  |  |  |  |  | 
| 5994 |  |  |  |  |  |  | # If the character is a hash mark, it means a comment. Bail | 
| 5995 |  |  |  |  |  |  | # out of the loop. | 
| 5996 |  |  |  |  |  |  | } elsif ( $char eq '#' ) { | 
| 5997 | 2 |  |  |  |  | 8 | last; | 
| 5998 |  |  |  |  |  |  |  | 
| 5999 |  |  |  |  |  |  | # Else we just put it in the token. | 
| 6000 |  |  |  |  |  |  | } else { | 
| 6001 | 829 |  |  |  |  | 2151 | $rslt[-1]{token} .= $char; | 
| 6002 |  |  |  |  |  |  | } | 
| 6003 |  |  |  |  |  |  |  | 
| 6004 |  |  |  |  |  |  | # If we're at the end of the buffer but we're inside quotes, | 
| 6005 |  |  |  |  |  |  | # we need to read another line. | 
| 6006 | 6305 | 100 | 66 |  |  | 15666 | if ( $inx >= $len && ( $absquote || $relquote ) ) { | 
|  |  |  | 100 |  |  |  |  | 
| 6007 | 2 | 50 |  |  |  | 10 | $buffer .= $self->_read_continuation( $in, | 
| 6008 |  |  |  |  |  |  | $absquote ? 'Unclosed single quote' : | 
| 6009 |  |  |  |  |  |  | 'Unclosed double quote' | 
| 6010 |  |  |  |  |  |  | ); | 
| 6011 | 2 |  |  |  |  | 6 | $len = length $buffer; | 
| 6012 |  |  |  |  |  |  | } | 
| 6013 |  |  |  |  |  |  |  | 
| 6014 |  |  |  |  |  |  | } | 
| 6015 |  |  |  |  |  |  |  | 
| 6016 |  |  |  |  |  |  | # We have run through the entire string to be tokenized. If | 
| 6017 |  |  |  |  |  |  | # there are unclosed quotes of either sort, we declare an error | 
| 6018 |  |  |  |  |  |  | # here. This should actually not happen, since we allow | 
| 6019 |  |  |  |  |  |  | # multi-line quotes, and if we have run out of input we catch it | 
| 6020 |  |  |  |  |  |  | # above. | 
| 6021 |  |  |  |  |  |  |  | 
| 6022 | 376 | 50 |  |  |  | 798 | $absquote and $self->wail( 'Unclosed terminal single quote' ); | 
| 6023 | 376 | 50 |  |  |  | 704 | $relquote and $self->wail( 'Unclosed terminal double quote' ); | 
| 6024 |  |  |  |  |  |  |  | 
| 6025 |  |  |  |  |  |  | # Replace leading punctuation with the corresponding method. | 
| 6026 |  |  |  |  |  |  |  | 
| 6027 |  |  |  |  |  |  | shift @rslt | 
| 6028 | 376 |  | 100 |  |  | 1515 | while @rslt && ! defined $rslt[0]{token}; | 
| 6029 | 376 | 50 | 66 |  |  | 2877 | if ( defined $rslt[0]{token} and | 
| 6030 |  |  |  |  |  |  | $rslt[0]{token} =~ s/ \A ( $command_equiv_re ) //smx ) { | 
| 6031 | 0 | 0 |  |  |  | 0 | if ( $rslt[0]{token} eq '' ) { | 
|  |  | 0 |  |  |  |  |  | 
| 6032 | 0 |  |  |  |  | 0 | $rslt[0]{token} = $command_equivalent{$1}; | 
| 6033 |  |  |  |  |  |  | } elsif ( $opt->{single} ) { | 
| 6034 |  |  |  |  |  |  | $rslt[0]{token} = join ' ', $command_equivalent{$1}, | 
| 6035 | 0 |  |  |  |  | 0 | $rslt[0]{token}; | 
| 6036 |  |  |  |  |  |  | } else { | 
| 6037 |  |  |  |  |  |  | unshift @rslt, { | 
| 6038 | 0 |  |  |  |  | 0 | token	=> $command_equivalent{$1}, | 
| 6039 |  |  |  |  |  |  | }; | 
| 6040 |  |  |  |  |  |  | } | 
| 6041 |  |  |  |  |  |  | } | 
| 6042 |  |  |  |  |  |  |  | 
| 6043 |  |  |  |  |  |  | # Go through our prospective tokens, keeping only those that | 
| 6044 |  |  |  |  |  |  | # were actually defined, and shuffling the redirects off into | 
| 6045 |  |  |  |  |  |  | # the redirect hash. | 
| 6046 |  |  |  |  |  |  |  | 
| 6047 | 376 |  |  |  |  | 742 | my (@tokens, %redir); | 
| 6048 | 376 |  |  |  |  | 698 | my $expand_tildes = 1; | 
| 6049 | 376 | 100 | 100 |  |  | 3206 | if ( defined $rslt[0]{token} | 
| 6050 |  |  |  |  |  |  | and my $kode = $self->can( $rslt[0]{token} ) ) { | 
| 6051 | 252 | 100 |  |  |  | 879 | if ( my $hash = $self->__get_attr( $kode, 'Tokenize' ) ) { | 
| 6052 | 2 |  |  |  |  | 17 | $expand_tildes = $hash->{expand_tilde}; | 
| 6053 |  |  |  |  |  |  | } | 
| 6054 |  |  |  |  |  |  | } | 
| 6055 | 376 |  |  |  |  | 1238 | foreach (@rslt) { | 
| 6056 | 1318 | 100 |  |  |  | 2760 | exists $_->{token} or next; | 
| 6057 | 966 | 100 | 66 |  |  | 2803 | if ($_->{redirect}) { | 
|  |  | 100 |  |  |  |  |  | 
| 6058 | 1 | 50 |  |  |  | 11 | if ( $_->{mode} eq '<' ) { | 
| 6059 |  |  |  |  |  |  | push @tokens, $self->_file_reader( | 
| 6060 | 0 |  |  |  |  | 0 | $_->{token}, { glob => 1 } ); | 
| 6061 |  |  |  |  |  |  | } else { | 
| 6062 | 1 |  |  |  |  | 2 | my $type = $_->{type}; | 
| 6063 |  |  |  |  |  |  | $redir{$type} = { | 
| 6064 |  |  |  |  |  |  | mode => $_->{mode}, | 
| 6065 |  |  |  |  |  |  | name => ($_->{expand} ? | 
| 6066 |  |  |  |  |  |  | $self->expand_tilde($_->{token}) : | 
| 6067 | 1 | 50 |  |  |  | 10 | $_->{token}), | 
| 6068 |  |  |  |  |  |  | }; | 
| 6069 |  |  |  |  |  |  | } | 
| 6070 |  |  |  |  |  |  | } elsif ( $expand_tildes && $_->{tilde} ) { | 
| 6071 | 12 |  |  |  |  | 85 | push @tokens, $self->expand_tilde( $_->{token} ); | 
| 6072 |  |  |  |  |  |  | } else { | 
| 6073 | 953 |  |  |  |  | 1943 | push @tokens, $_->{token}; | 
| 6074 |  |  |  |  |  |  | } | 
| 6075 |  |  |  |  |  |  | } | 
| 6076 |  |  |  |  |  |  |  | 
| 6077 |  |  |  |  |  |  | # With the {single} and {noredirect} options both asserted, | 
| 6078 |  |  |  |  |  |  | # there is only one token, so we return it directly. | 
| 6079 |  |  |  |  |  |  |  | 
| 6080 | 372 | 50 | 66 |  |  | 1098 | ($opt->{single} && $opt->{noredirect}) and return $tokens[0]; | 
| 6081 |  |  |  |  |  |  |  | 
| 6082 |  |  |  |  |  |  | # With the {noredirect} option asserted, we just return a | 
| 6083 |  |  |  |  |  |  | # reference to the tokens found. | 
| 6084 |  |  |  |  |  |  |  | 
| 6085 | 341 | 50 |  |  |  | 726 | $opt->{noredirect} and return \@tokens; | 
| 6086 |  |  |  |  |  |  |  | 
| 6087 |  |  |  |  |  |  | # Otherwise we return a list, with a reference to the token list | 
| 6088 |  |  |  |  |  |  | # as the first element, and a reference to the redirect hash as | 
| 6089 |  |  |  |  |  |  | # the second element. | 
| 6090 |  |  |  |  |  |  |  | 
| 6091 | 341 |  |  |  |  | 2197 | return (\@tokens, \%redir); | 
| 6092 |  |  |  |  |  |  | } | 
| 6093 |  |  |  |  |  |  |  | 
| 6094 |  |  |  |  |  |  | # Retrieve the value of a variable. | 
| 6095 |  |  |  |  |  |  | sub _tokenize_var { | 
| 6096 | 74 |  |  | 74 |  | 376 | my ($self, $name, $args, $relquote, $indirect) = @_; | 
| 6097 |  |  |  |  |  |  |  | 
| 6098 | 74 | 0 | 33 |  |  | 313 | defined $name and $name ne '' | 
|  |  | 50 |  |  |  |  |  | 
| 6099 |  |  |  |  |  |  | or return $indirect ? '' : undef; | 
| 6100 |  |  |  |  |  |  |  | 
| 6101 | 74 | 100 |  |  |  | 221 | $special{$name} and do { | 
| 6102 | 19 |  |  |  |  | 95 | my $val = $special{$name}->($args, $relquote); | 
| 6103 | 19 | 50 | 33 |  |  | 113 | return ($indirect && ref $val) ? '' : $val; | 
| 6104 |  |  |  |  |  |  | }; | 
| 6105 |  |  |  |  |  |  |  | 
| 6106 | 55 | 100 |  |  |  | 246 | $name !~ m/ \D /smx | 
| 6107 |  |  |  |  |  |  | and return $args->[$name - 1]; | 
| 6108 |  |  |  |  |  |  |  | 
| 6109 | 40 |  |  |  |  | 128 | my $value = $self->_attribute_value( $name ); | 
| 6110 | 40 | 100 |  |  |  | 134 | NULL_REF eq ref $value | 
| 6111 |  |  |  |  |  |  | or return $value; | 
| 6112 |  |  |  |  |  |  |  | 
| 6113 |  |  |  |  |  |  | exists $self->{exported}{$name} | 
| 6114 | 34 | 100 |  |  |  | 111 | and return $self->{exported}{$name}; | 
| 6115 |  |  |  |  |  |  |  | 
| 6116 |  |  |  |  |  |  | defined $ENV{$name} | 
| 6117 | 32 | 100 |  |  |  | 126 | and return $ENV{$name}; | 
| 6118 |  |  |  |  |  |  |  | 
| 6119 | 14 |  |  |  |  | 31 | foreach my $frame ( reverse @{ $self->{frame} } ) { | 
|  | 14 |  |  |  |  | 53 |  | 
| 6120 |  |  |  |  |  |  | defined $frame->{define}{$name} | 
| 6121 | 17 | 100 |  |  |  | 56 | and return $frame->{define}{$name}; | 
| 6122 |  |  |  |  |  |  | } | 
| 6123 |  |  |  |  |  |  |  | 
| 6124 | 11 |  |  |  |  | 25 | return; | 
| 6125 |  |  |  |  |  |  | } | 
| 6126 |  |  |  |  |  |  |  | 
| 6127 |  |  |  |  |  |  | } | 
| 6128 |  |  |  |  |  |  |  | 
| 6129 |  |  |  |  |  |  | # Apply case modification to the arguments | 
| 6130 |  |  |  |  |  |  | # NOTE that the argument list is modified in-place. I'm a little | 
| 6131 |  |  |  |  |  |  | # surprised that this didn't tickle Perl::Critic. | 
| 6132 |  |  |  |  |  |  | sub _case_mod { | 
| 6133 | 3756 |  |  | 3756 |  | 5140 | my $self = shift; | 
| 6134 | 3756 |  |  |  |  | 5823 | foreach ( @_ ) { | 
| 6135 |  |  |  |  |  |  | $self->{_case_mod}{case} | 
| 6136 | 3759 | 100 |  |  |  | 7187 | and $_ = $self->{_case_mod}{case}->( $self, $_ ); | 
| 6137 | 3759 |  |  |  |  | 4813 | my $code; | 
| 6138 |  |  |  |  |  |  | $code = delete $self->{_case_mod}{single} | 
| 6139 | 3759 | 100 |  |  |  | 7859 | and $_ = $code->( $self, $_ ); | 
| 6140 |  |  |  |  |  |  | } | 
| 6141 | 3756 |  |  |  |  | 5526 | return; | 
| 6142 |  |  |  |  |  |  | } | 
| 6143 |  |  |  |  |  |  |  | 
| 6144 |  |  |  |  |  |  | #	$self->wail(...) | 
| 6145 |  |  |  |  |  |  | # | 
| 6146 |  |  |  |  |  |  | #	Either die or croak with the arguments, depending on the value | 
| 6147 |  |  |  |  |  |  | #	of the 'warning' attribute. If we die, a trailing period and | 
| 6148 |  |  |  |  |  |  | #	newline are provided if necessary. If we croak, any trailing | 
| 6149 |  |  |  |  |  |  | #	punctuation and newline are stripped. | 
| 6150 |  |  |  |  |  |  |  | 
| 6151 |  |  |  |  |  |  | sub wail { | 
| 6152 | 18 |  |  | 18 | 1 | 107 | my ($self, @args) = @_; | 
| 6153 | 18 |  |  |  |  | 234 | $self->{_warner}->wail( @args ); | 
| 6154 | 0 |  |  |  |  | 0 | return;	# We can't hit this, but Perl::Critic does not know that. | 
| 6155 |  |  |  |  |  |  | } | 
| 6156 |  |  |  |  |  |  |  | 
| 6157 |  |  |  |  |  |  | #	$self->__wail(...) | 
| 6158 |  |  |  |  |  |  | # | 
| 6159 |  |  |  |  |  |  | #	either wail() or whinge() depending on error_out. | 
| 6160 |  |  |  |  |  |  | sub __wail { | 
| 6161 | 1 |  |  | 1 |  | 6 | my ($self, @args) = @_; | 
| 6162 | 1 | 50 |  |  |  | 6 | if ( $self->get( 'error_out' ) ) { | 
| 6163 | 1 |  |  |  |  | 5 | $self->{_warner}->wail( @args ); | 
| 6164 |  |  |  |  |  |  | } else { | 
| 6165 | 0 |  |  |  |  | 0 | $self->{_warner}->whinge( @args ); | 
| 6166 |  |  |  |  |  |  | } | 
| 6167 | 0 |  |  |  |  | 0 | return; | 
| 6168 |  |  |  |  |  |  | } | 
| 6169 |  |  |  |  |  |  |  | 
| 6170 |  |  |  |  |  |  | #	$self->weep(...) | 
| 6171 |  |  |  |  |  |  | # | 
| 6172 |  |  |  |  |  |  | #	Die with a stack dump (Carp::confess). | 
| 6173 |  |  |  |  |  |  |  | 
| 6174 |  |  |  |  |  |  | sub weep { | 
| 6175 | 0 |  |  | 0 | 1 | 0 | my ($self, @args) = @_; | 
| 6176 | 0 |  |  |  |  | 0 | $self->{_warner}->weep( @args ); | 
| 6177 | 0 |  |  |  |  | 0 | return;	# We can't hit this, but Perl::Critic does not know that. | 
| 6178 |  |  |  |  |  |  | } | 
| 6179 |  |  |  |  |  |  |  | 
| 6180 |  |  |  |  |  |  | #	$self->whinge(...) | 
| 6181 |  |  |  |  |  |  | # | 
| 6182 |  |  |  |  |  |  | #	Either warn or carp with the arguments, depending on the value | 
| 6183 |  |  |  |  |  |  | #	of the 'warn' attribute. If we warn, a trailing period and | 
| 6184 |  |  |  |  |  |  | #	newline are provided if necessary. If we carp, any trailing | 
| 6185 |  |  |  |  |  |  | #	punctuation and newline are stripped. | 
| 6186 |  |  |  |  |  |  |  | 
| 6187 |  |  |  |  |  |  | sub whinge { | 
| 6188 | 3 |  |  | 3 | 1 | 12 | my ($self, @args) = @_; | 
| 6189 | 3 |  |  |  |  | 24 | $self->{_warner}->whinge( @args ); | 
| 6190 | 3 |  |  |  |  | 11 | return; | 
| 6191 |  |  |  |  |  |  | } | 
| 6192 |  |  |  |  |  |  |  | 
| 6193 |  |  |  |  |  |  | 1; | 
| 6194 |  |  |  |  |  |  |  | 
| 6195 |  |  |  |  |  |  | __END__ |