Rashed Abdel-Tawab | 4db47f4 | 2019-09-06 10:38:22 -0700 | [diff] [blame] | 1 | package re; |
| 2 | |
| 3 | # pragma for controlling the regexp engine |
| 4 | use strict; |
| 5 | use warnings; |
| 6 | |
| 7 | our $VERSION = "0.34"; |
| 8 | our @ISA = qw(Exporter); |
| 9 | our @EXPORT_OK = ('regmust', |
| 10 | qw(is_regexp regexp_pattern |
| 11 | regname regnames regnames_count)); |
| 12 | our %EXPORT_OK = map { $_ => 1 } @EXPORT_OK; |
| 13 | |
| 14 | my %bitmask = ( |
| 15 | taint => 0x00100000, # HINT_RE_TAINT |
| 16 | eval => 0x00200000, # HINT_RE_EVAL |
| 17 | ); |
| 18 | |
| 19 | my $flags_hint = 0x02000000; # HINT_RE_FLAGS |
| 20 | my $PMMOD_SHIFT = 0; |
| 21 | my %reflags = ( |
| 22 | m => 1 << ($PMMOD_SHIFT + 0), |
| 23 | s => 1 << ($PMMOD_SHIFT + 1), |
| 24 | i => 1 << ($PMMOD_SHIFT + 2), |
| 25 | x => 1 << ($PMMOD_SHIFT + 3), |
| 26 | xx => 1 << ($PMMOD_SHIFT + 4), |
| 27 | n => 1 << ($PMMOD_SHIFT + 5), |
| 28 | p => 1 << ($PMMOD_SHIFT + 6), |
| 29 | strict => 1 << ($PMMOD_SHIFT + 10), |
| 30 | # special cases: |
| 31 | d => 0, |
| 32 | l => 1, |
| 33 | u => 2, |
| 34 | a => 3, |
| 35 | aa => 4, |
| 36 | ); |
| 37 | |
| 38 | sub setcolor { |
| 39 | eval { # Ignore errors |
| 40 | require Term::Cap; |
| 41 | |
| 42 | my $terminal = Tgetent Term::Cap ({OSPEED => 9600}); # Avoid warning. |
| 43 | my $props = $ENV{PERL_RE_TC} || 'md,me,so,se,us,ue'; |
| 44 | my @props = split /,/, $props; |
| 45 | my $colors = join "\t", map {$terminal->Tputs($_,1)} @props; |
| 46 | |
| 47 | $colors =~ s/\0//g; |
| 48 | $ENV{PERL_RE_COLORS} = $colors; |
| 49 | }; |
| 50 | if ($@) { |
| 51 | $ENV{PERL_RE_COLORS} ||= qq'\t\t> <\t> <\t\t'; |
| 52 | } |
| 53 | |
| 54 | } |
| 55 | |
| 56 | my %flags = ( |
| 57 | COMPILE => 0x0000FF, |
| 58 | PARSE => 0x000001, |
| 59 | OPTIMISE => 0x000002, |
| 60 | TRIEC => 0x000004, |
| 61 | DUMP => 0x000008, |
| 62 | FLAGS => 0x000010, |
| 63 | TEST => 0x000020, |
| 64 | |
| 65 | EXECUTE => 0x00FF00, |
| 66 | INTUIT => 0x000100, |
| 67 | MATCH => 0x000200, |
| 68 | TRIEE => 0x000400, |
| 69 | |
| 70 | EXTRA => 0xFF0000, |
| 71 | TRIEM => 0x010000, |
| 72 | OFFSETS => 0x020000, |
| 73 | OFFSETSDBG => 0x040000, |
| 74 | STATE => 0x080000, |
| 75 | OPTIMISEM => 0x100000, |
| 76 | STACK => 0x280000, |
| 77 | BUFFERS => 0x400000, |
| 78 | GPOS => 0x800000, |
| 79 | ); |
| 80 | $flags{ALL} = -1 & ~($flags{OFFSETS}|$flags{OFFSETSDBG}|$flags{BUFFERS}); |
| 81 | $flags{All} = $flags{all} = $flags{DUMP} | $flags{EXECUTE}; |
| 82 | $flags{Extra} = $flags{EXECUTE} | $flags{COMPILE} | $flags{GPOS}; |
| 83 | $flags{More} = $flags{MORE} = $flags{All} | $flags{TRIEC} | $flags{TRIEM} | $flags{STATE}; |
| 84 | $flags{State} = $flags{DUMP} | $flags{EXECUTE} | $flags{STATE}; |
| 85 | $flags{TRIE} = $flags{DUMP} | $flags{EXECUTE} | $flags{TRIEC}; |
| 86 | |
| 87 | if (defined &DynaLoader::boot_DynaLoader) { |
| 88 | require XSLoader; |
| 89 | XSLoader::load(); |
| 90 | } |
| 91 | # else we're miniperl |
| 92 | # We need to work for miniperl, because the XS toolchain uses Text::Wrap, which |
| 93 | # uses re 'taint'. |
| 94 | |
| 95 | sub _load_unload { |
| 96 | my ($on)= @_; |
| 97 | if ($on) { |
| 98 | # We call install() every time, as if we didn't, we wouldn't |
| 99 | # "see" any changes to the color environment var since |
| 100 | # the last time it was called. |
| 101 | |
| 102 | # install() returns an integer, which if casted properly |
| 103 | # in C resolves to a structure containing the regexp |
| 104 | # hooks. Setting it to a random integer will guarantee |
| 105 | # segfaults. |
| 106 | $^H{regcomp} = install(); |
| 107 | } else { |
| 108 | delete $^H{regcomp}; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | sub bits { |
| 113 | my $on = shift; |
| 114 | my $bits = 0; |
| 115 | my $turning_all_off = ! @_ && ! $on; |
| 116 | if ($turning_all_off) { |
| 117 | |
| 118 | # Pretend were called with certain parameters, which are best dealt |
| 119 | # with that way. |
| 120 | push @_, keys %bitmask; # taint and eval |
| 121 | push @_, 'strict'; |
| 122 | } |
| 123 | |
| 124 | # Process each subpragma parameter |
| 125 | ARG: |
| 126 | foreach my $idx (0..$#_){ |
| 127 | my $s=$_[$idx]; |
| 128 | if ($s eq 'Debug' or $s eq 'Debugcolor') { |
| 129 | setcolor() if $s =~/color/i; |
| 130 | ${^RE_DEBUG_FLAGS} = 0 unless defined ${^RE_DEBUG_FLAGS}; |
| 131 | for my $idx ($idx+1..$#_) { |
| 132 | if ($flags{$_[$idx]}) { |
| 133 | if ($on) { |
| 134 | ${^RE_DEBUG_FLAGS} |= $flags{$_[$idx]}; |
| 135 | } else { |
| 136 | ${^RE_DEBUG_FLAGS} &= ~ $flags{$_[$idx]}; |
| 137 | } |
| 138 | } else { |
| 139 | require Carp; |
| 140 | Carp::carp("Unknown \"re\" Debug flag '$_[$idx]', possible flags: ", |
| 141 | join(", ",sort keys %flags ) ); |
| 142 | } |
| 143 | } |
| 144 | _load_unload($on ? 1 : ${^RE_DEBUG_FLAGS}); |
| 145 | last; |
| 146 | } elsif ($s eq 'debug' or $s eq 'debugcolor') { |
| 147 | setcolor() if $s =~/color/i; |
| 148 | _load_unload($on); |
| 149 | last; |
| 150 | } elsif (exists $bitmask{$s}) { |
| 151 | $bits |= $bitmask{$s}; |
| 152 | } elsif ($EXPORT_OK{$s}) { |
| 153 | require Exporter; |
| 154 | re->export_to_level(2, 're', $s); |
| 155 | } elsif ($s eq 'strict') { |
| 156 | if ($on) { |
| 157 | $^H{reflags} |= $reflags{$s}; |
| 158 | warnings::warnif('experimental::re_strict', |
| 159 | "\"use re 'strict'\" is experimental"); |
| 160 | |
| 161 | # Turn on warnings if not already done. |
| 162 | if (! warnings::enabled('regexp')) { |
| 163 | require warnings; |
| 164 | warnings->import('regexp'); |
| 165 | $^H{re_strict} = 1; |
| 166 | } |
| 167 | } |
| 168 | else { |
| 169 | $^H{reflags} &= ~$reflags{$s} if $^H{reflags}; |
| 170 | |
| 171 | # Turn off warnings if we turned them on. |
| 172 | warnings->unimport('regexp') if $^H{re_strict}; |
| 173 | } |
| 174 | if ($^H{reflags}) { |
| 175 | $^H |= $flags_hint; |
| 176 | } |
| 177 | else { |
| 178 | $^H &= ~$flags_hint; |
| 179 | } |
| 180 | } elsif ($s =~ s/^\///) { |
| 181 | my $reflags = $^H{reflags} || 0; |
| 182 | my $seen_charset; |
| 183 | my $x_count = 0; |
| 184 | while ($s =~ m/( . )/gx) { |
| 185 | local $_ = $1; |
| 186 | if (/[adul]/) { |
| 187 | # The 'a' may be repeated; hide this from the rest of the |
| 188 | # code by counting and getting rid of all of them, then |
| 189 | # changing to 'aa' if there is a repeat. |
| 190 | if ($_ eq 'a') { |
| 191 | my $sav_pos = pos $s; |
| 192 | my $a_count = $s =~ s/a//g; |
| 193 | pos $s = $sav_pos - 1; # -1 because got rid of the 'a' |
| 194 | if ($a_count > 2) { |
| 195 | require Carp; |
| 196 | Carp::carp( |
| 197 | qq 'The "a" flag may only appear a maximum of twice' |
| 198 | ); |
| 199 | } |
| 200 | elsif ($a_count == 2) { |
| 201 | $_ = 'aa'; |
| 202 | } |
| 203 | } |
| 204 | if ($on) { |
| 205 | if ($seen_charset) { |
| 206 | require Carp; |
| 207 | if ($seen_charset ne $_) { |
| 208 | Carp::carp( |
| 209 | qq 'The "$seen_charset" and "$_" flags ' |
| 210 | .qq 'are exclusive' |
| 211 | ); |
| 212 | } |
| 213 | else { |
| 214 | Carp::carp( |
| 215 | qq 'The "$seen_charset" flag may not appear ' |
| 216 | .qq 'twice' |
| 217 | ); |
| 218 | } |
| 219 | } |
| 220 | $^H{reflags_charset} = $reflags{$_}; |
| 221 | $seen_charset = $_; |
| 222 | } |
| 223 | else { |
| 224 | delete $^H{reflags_charset} |
| 225 | if defined $^H{reflags_charset} |
| 226 | && $^H{reflags_charset} == $reflags{$_}; |
| 227 | } |
| 228 | } elsif (exists $reflags{$_}) { |
| 229 | if ($_ eq 'x') { |
| 230 | $x_count++; |
| 231 | if ($x_count > 2) { |
| 232 | require Carp; |
| 233 | Carp::carp( |
| 234 | qq 'The "x" flag may only appear a maximum of twice' |
| 235 | ); |
| 236 | } |
| 237 | elsif ($x_count == 2) { |
| 238 | $_ = 'xx'; # First time through got the /x |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | $on |
| 243 | ? $reflags |= $reflags{$_} |
| 244 | : ($reflags &= ~$reflags{$_}); |
| 245 | } else { |
| 246 | require Carp; |
| 247 | Carp::carp( |
| 248 | qq'Unknown regular expression flag "$_"' |
| 249 | ); |
| 250 | next ARG; |
| 251 | } |
| 252 | } |
| 253 | ($^H{reflags} = $reflags or defined $^H{reflags_charset}) |
| 254 | ? $^H |= $flags_hint |
| 255 | : ($^H &= ~$flags_hint); |
| 256 | } else { |
| 257 | require Carp; |
| 258 | Carp::carp("Unknown \"re\" subpragma '$s' (known ones are: ", |
| 259 | join(', ', map {qq('$_')} 'debug', 'debugcolor', sort keys %bitmask), |
| 260 | ")"); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | if ($turning_all_off) { |
| 265 | _load_unload(0); |
| 266 | $^H{reflags} = 0; |
| 267 | $^H{reflags_charset} = 0; |
| 268 | $^H &= ~$flags_hint; |
| 269 | } |
| 270 | |
| 271 | $bits; |
| 272 | } |
| 273 | |
| 274 | sub import { |
| 275 | shift; |
| 276 | $^H |= bits(1, @_); |
| 277 | } |
| 278 | |
| 279 | sub unimport { |
| 280 | shift; |
| 281 | $^H &= ~ bits(0, @_); |
| 282 | } |
| 283 | |
| 284 | 1; |
| 285 | |
| 286 | __END__ |
| 287 | |