File Coverage

/root/.cpan/build/Text-Starfish-1.37-0/blib/lib/Text/Starfish.pm
Criterion Covered Total %
statement 1468 1733 84.7
branch 258 466 55.3
condition 44 72 61.1
subroutine 332 354 93.7
pod 32 65 49.2
total 2134 2690 79.3


line stmt bran cond sub pod time code
1             # Starfish - Perl-based System for Preprocessing and Text-Embedded Programming
2             #
3             # (c) 2001-2020 Vlado Keselj http://web.cs.dal.ca/~vlado vlado@dnlp.ca
4             # and contributing authors
5             #
6             # See the documentation following the code. You can also use the
7             # command "perldoc Starfish.pm".
8              
9             package Text::Starfish;
10 4     4   6619 use vars qw($NAME $ABSTRACT $VERSION);
  4         8  
  4         289  
11             $NAME = 'Text::Starfish';
12             $ABSTRACT = 'Perl-based System for Preprocessing and Text-Embedded Programming';
13             $VERSION = '1.37';
14              
15 3     3   19 use strict;
  3         5  
  3         59  
16 3     3   1612 use POSIX;
  3         26292  
  3         14  
17 3     3   8359 use Carp;
  3         6  
  3         180  
18 3     3   19 use Cwd qw(cwd);
  3         19  
  3         139  
19 3     3   17 use Exporter;
  3         4  
  3         135  
20 3     3   32 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); # Exporter vars
  3         5  
  3         445  
21              
22             @ISA = qw(Exporter);
23             %EXPORT_TAGS = ( 'all' => [qw(
24             add_hook appendfile echo file_modification_date
25             file_modification_time getfile getmakefilelist get_verbatim_file
26             getinclude htmlquote include
27             last_update putfile read_records read_starfish_conf rm_hook set_out_delimiters
28             sfish_add_tag sfish_ignore_outer starfish_cmd make_gen_dirs_to_generate
29             make_add_dirs_to_generate_if_needed
30             ) ] );
31             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
32             @EXPORT = @{ $EXPORT_TAGS{'all'} };
33              
34             # Used in starfishing Makefiles
35 3     3   19 use vars qw(@DirGenerateIfNeeded);
  3         6  
  3         152  
36              
37             # non-exported package globals
38 3     3   15 use vars qw($GlobalREPLACE);
  3         4  
  3         33085  
39              
40             sub appendfile($@);
41             sub getfile($ );
42             sub getmakefilelist($$);
43             sub htmlquote($ );
44             sub putfile($@);
45             sub read_records($ );
46             sub starfish_cmd(@);
47              
48             sub new($@) {
49 45     45 1 137 my $proto = shift;
50 45   33     276 my $class = ref($proto) || $proto;
51 45         143 my $self = {};
52 45         118 bless($self, $class);
53              
54 45         200 $self->{Loops} = 1;
55 45         127 my $copyhooks = '';
56 45         164 foreach (@_) {
57 2 50       47 if (/^-infile=(.*)$/) { $self->{INFILE} = $1 }
  2 0       10  
58 0         0 elsif (/^-copyhooks$/) { $copyhooks = 1 }
59 0         0 else { _croak("unknown new option: $_") }
60             }
61              
62 45 50       135 if ($copyhooks) {
63 0 0       0 _croak("new: cannot copyhooks if Star is not there") unless
64             ref($::Star) eq 'Text::Starfish';
65 0         0 $self->{Style} = $::Star->{Style};
66 0         0 $self->{CodePreparation} = $::Star->{CodePreparation};
67 0         0 $self->{LineComment} = $::Star->{LineComment};
68 0         0 $self->{OutDelimiters} = $::Star->{OutDelimiters};
69 0         0 $self->{IgnoreOuter} = $::Star->{IgnoreOuter};
70 0         0 $self->{hook} = [ @{ $::Star->{hook} } ];
  0         0  
71             }
72              
73 45 50       232 $self->set_style() unless $copyhooks;
74              
75 45         93 return $self;
76             }
77              
78             sub starfish_cmd(@) {
79 43     43 1 10282 my $sf = Text::Starfish->new();
80 43         87 my @tmp = ();
81 43         104 foreach (@_) {
82 119 100       701 if (/^-e=?/) { $sf->{INITIAL_CODE} = $' }
  32 100       178  
    100          
    100          
    50          
83 2         39 elsif (/^-mode=/) { $sf->{NEW_FILE_MODE} = $' }
84 25         110 elsif (/^-o=/) { $sf->{OUTFILE} = $' }
85 16         47 elsif (/^-replace$/) { $sf->{REPLACE} = $GlobalREPLACE = 1; }
86 0         0 elsif (/^-v$/) { print "$NAME, version $VERSION, $ABSTRACT\n"; exit 0; }
  0         0  
87 44         118 else { push @tmp, $_ }
88             }
89              
90 43 100 66     159 if (defined $sf->{NEW_FILE_MODE} and $sf->{NEW_FILE_MODE} =~ /^0/)
91 2         22 { $sf->{NEW_FILE_MODE} = oct($sf->{NEW_FILE_MODE}) }
92              
93 43         156 $sf->process_files(@tmp);
94 43         561 return $sf;
95             }
96              
97 1     1 1 4 sub include($@) { $::O .= getinclude(@_); return 1; }
  1         25  
98              
99             sub getinclude($@) {
100 2     2 1 8 my $sf = loadinclude(@_);
101 2 100       10 return '' unless defined $sf;
102 1         14 $sf->digest();
103 1         8 return $sf->{Out};
104             }
105              
106             sub loadinclude($@) {
107 2     2 1 11 my $infile = '';
108 2         6 my @args = ();
109 2         3 my $replace = 1;
110 2         7 my $require = '';
111 2         8 foreach (@_) {
112 2 50 33     46 if (/^-replace$/) { $replace = 1 }
  0 50       0  
    50          
    50          
113 0         0 elsif (/^-noreplace$/) { $replace = '' }
114 0         0 elsif (/^-require$/) { $require = 1 }
115 2         6 elsif (!/^-/ && $infile eq '') { $infile = $_ }
116 0         0 else { push @args, $_ }
117             }
118              
119 2         13 my $sf = Text::Starfish->new("-infile=$infile", @args);
120 2         5 $sf->{REPLACE} = $replace;
121              
122 2 100 66     69 if ($sf->{INFILE} eq '' or ! -r $sf->{INFILE} ) {
123 1 50       13 if ($require) { _croak("cannot getinclude file: ($sf->{INFILE})") }
  0         0  
124 1         8 return undef;
125             }
126              
127 1         7 $sf->{data} = getfile $sf->{INFILE};
128 1         4 return $sf;
129             }
130              
131             sub process_files {
132 43     43 1 72 my $self = shift;
133 43         102 my @args = @_;
134              
135 43 50 66     191 if (defined $self->{REPLACE} and !defined $self->{OUTFILE})
136 0         0 { _croak("Starfish:output file required for replace") }
137              
138 43         77 my $FileCount=0;
139 43         147 $self->eval1($self->{INITIAL_CODE}, 'initial');
140              
141 43         123 while (@args) {
142 44         198 $self->{INFILE} = shift @args;
143 44         76 ++$FileCount;
144 44         140 $self->set_style();
145 44         128 $self->{data} = getfile $self->{INFILE};
146              
147             # *123* we need to forbid defining an outfile externally as well as
148             # internally:
149 44 100       208 my $outfileExternal = exists($self->{OUTFILE}) ? $self->{OUTFILE} : '';
150              
151 44         92 my $ExistingText = '';
152 44 100       588 if (! defined $self->{OUTFILE}) {
    50          
    100          
153 19         48 $ExistingText = $self->{data};
154 19         316 $self->{LastUpdateTime} = (stat $self->{INFILE})[9];
155             }
156             elsif ($FileCount > 1) {
157 0         0 $ExistingText = '';
158 0         0 $self->{LastUpdateTime} = time;
159             }
160             elsif (! -f $self->{OUTFILE}) {
161 23         70 $ExistingText = '';
162 23         74 $self->{LastUpdateTime} = time;
163             }
164             else {
165 2         20 $ExistingText = getfile $self->{OUTFILE};
166 2         42 $self->{LastUpdateTime} = (stat $self->{OUTFILE})[9];
167             }
168              
169 44         216 $self->digest();
170              
171             # see *123* above
172 44 50 66     205 if ($outfileExternal ne '' and $outfileExternal ne $self->{OUTFILE})
173 0         0 { _croak("OUTFILE defined externally ($outfileExternal) and ".
174             "internally ($self->{OUTFILE})") }
175              
176 44         92 my $InFile = $self->{INFILE};
177 44 100 100     193 if ($FileCount==1 && defined $self->{OUTFILE}) {
178             # touch the outfile if it does not exist
179 25 100       384 if ( ! -f $self->{OUTFILE} ) {
    50          
180 23         108 putfile $self->{OUTFILE};
181 23         331 my $infile_mode = (stat $InFile)[2];
182 23 100       120 if (defined $self->{NEW_FILE_MODE}) {
183 2         42 chmod $self->{NEW_FILE_MODE}, $self->{OUTFILE}}
184 21         351 else { chmod $infile_mode, $self->{OUTFILE} }
185             }
186             elsif (defined $self->{NEW_FILE_MODE}) {
187 0         0 chmod $self->{NEW_FILE_MODE}, $self->{OUTFILE} }
188             }
189              
190             # write the text if changed
191 44 100       176 if ($ExistingText ne $self->{Out}) {
    50          
192 39 100       95 if (defined $self->{OUTFILE}) {
193             # If the OutFile is defined, we may have to play with
194             # permissions in order to write. Be careful! Allow
195             # unallowed write only on outfile and if -mode is
196             # specified
197 23         282 my $mode = ((stat $self->{OUTFILE})[2]);
198 23 100 66     140 if (($mode & 0200) == 0 and defined $self->{NEW_FILE_MODE}) {
199 1         15 chmod $mode|0200, $self->{OUTFILE};
200 1 50       15 if ($FileCount==1) { putfile $self->{OUTFILE}, $self->{Out} }
  1         13  
201 0         0 else { appendfile $self->{OUTFILE}, $self->{Out} }
202 1         25 chmod $mode, $self->{OUTFILE};
203             } else {
204 22 50       47 if ($FileCount==1) { putfile $self->{OUTFILE}, $self->{Out} }
  22         73  
205 0         0 else { appendfile $self->{OUTFILE}, $self->{Out} }
206             }
207             }
208             else {
209 16         51 putfile $InFile, $self->{Out};
210 16 50       146 chmod $self->{NEW_FILE_MODE}, $InFile if defined $self->{NEW_FILE_MODE};
211             }
212             }
213             elsif (defined $self->{NEW_FILE_MODE}) {
214 0 0       0 if (defined $self->{OUTFILE}) {
215 0         0 chmod $self->{NEW_FILE_MODE}, $self->{OUTFILE} }
216 0         0 else { chmod $self->{NEW_FILE_MODE}, $InFile }
217             }
218             } # end of while (@args)
219              
220             } # end of method process_files
221              
222             # eval_dispatch should see how to properly call the evaluator, or replacement
223             # eventually it should also be used for string-based evaluators
224             sub eval_dispatch {
225 27     27 0 45 my $self = shift;
226 27         47 my $hook = $self->{hook}->[$self->{ttype}];
227 27 50       55 if ($hook->{ht} eq 'regex') {
228 27         50 local $::Star = $self;
229 27         47 my $c = $self->{args}->[0];
230 27         604 my $r = &{ $self->{hook}->[$self->{ttype}]->{replace} }
231 27         41 ( $self, @{ $self->{args} } );
  27         63  
232 27 100       114 return $r if $self->{REPLACE};
233 2 50       15 return $c if $r eq '';
234 2         6 return $c."".$self->_output_wrap($r);
235             }
236 0         0 _croak("Hook type not evaluated: ht=($hook->{ht})");
237             }
238              
239             sub digest {
240 45     45 1 81 my $self = shift;
241 45         94 $self->{CurrentLoop} = 1;
242 45         107 my $savedcontent = $self->{data};
243            
244             START: # The main scanning loop
245 52         148 $self->{Out} = '';
246 52         154 $self->scan();
247 52         130 while ($self->{ttype} != -1) {
248 763 100       1555 if ($self->{ttype} > -1) {
249 717         1233 my $hook = $self->{hook}->[$self->{ttype}];
250 717 100       1321 if ($hook->{ht} eq 'regex') {
    100          
251 27         63 $self->{Out} .= $self->eval_dispatch;
252             }
253             # old style regex evaluator, should be removed, but first
254             # fix problems with python style
255 690         1312 elsif ( @{$self->{args}} ) {
256 507         6604 $self->{Out}.= &{$self->{hook}->[$self->{ttype}]->{replace}}
257 507         754 ( $self, @{ $self->{args} } );
  507         792  
258             }
259             else {
260 183         733 $self->{Out}.= &{$self->{hook}->[$self->{ttype}]->{f}}
261 183         377 ( $self, $self->{prefix}, $self->{currenttoken}, $self->{suffix});
262             }
263             } # else $ttype < -1 (outer text)
264 46         102 else { $self->_process_outer( $self->{currenttoken} ) }
265 763         2114 $self->scan();
266             }
267            
268 52 100       134 if ($self->{CurrentLoop} < $self->{Loops}) {
269 7         27 ++$self->{CurrentLoop};
270 7 100       18 if ($self->{REPLACE}) { # in replace mode interate with
271 1         3 $self->{data} = $savedcontent; # original input
272 1         7 goto START;
273             }
274 6         13 $self->{data} = $self->{Out};
275 6 50       20 if ($savedcontent ne $self->{Out})
276 6         12 { $self->{LastUpdateTime} = time }
277 6         17 putfile 'sfish.debug', $self->{data};
278 6         48 goto START;
279             }
280              
281             # Final routines, if defined
282 45 100       125 if (defined($self->{Final})) {
283 2         20 my @a = @{ $self->{Final} };
  2         19  
284 2         8 local $::Star = $self;
285 2         6 for my $f (@a) {
286 2         4 $self->{Out} = &{$f}($self->{Out}); }
  2         55  
287             }
288            
289             # Related to the macro concept (e.g. code folding)
290 45 100       137 if (defined $self->{macrosdefined}) {
291 6         18 my ($m, $s);
292 6         8 for $m (sort keys %{$self->{Macros}}) {
  6         44  
293 10         19 $s = $self->{Macros}->{$m};
294 10 50       38 if ($s =~ /\n/) {
295 10         33 my $p1 = "$`$&"; $s = $';
  10         24  
296 10 50       16 if ($s) { $s = $p1.wrap($s) }
  10         37  
297 0         0 else { $s = $p1 }
298             }
299 10         35 $self->{Out}.= $self->{MprefAuxDefine}.$s.$self->{MsufAuxDefine};
300             }
301             }
302             } # end of sub digest
303              
304             # process outer text; by default, it should be appended to $self->{Out}
305             sub _process_outer {
306 763     763   1147 my $self = shift; my $outer = shift;
  763         1524  
307 763 100 100     2366 if ($self->{REPLACE} && $self->{IgnoreOuter}) { }
308 743         1492 else { $self->{Out} .= $outer }
309             # Comment: something should be done if IgnoreOuter is true and
310             # not in REPLACE mode; maybe comment-out outer text? (todo?)
311             }
312              
313             sub _index {
314 3238     3238   4726 my $str = shift;
315 3238         4058 my $subs = shift;
316 3238 100       5013 if (ref($subs) eq 'Regexp') {
317 93 100       876 if ($str =~ $subs) { return (length($`),length($&)) }
  25         79  
318 68         165 else { return (-1,0) }
319             }
320 3145         11416 else { return (index($str, $subs), length($subs)) }
321             }
322              
323             # $self->{ttype}: -1 EOF
324             # -2 outer text (but also handled directly)
325             sub scan {
326 815     815 0 1144 my $self = shift;
327              
328 815         1519 $self->{prefix} = $self->{suffix} = '';
329 815         1636 $self->{args} = [];
330 815 100       1672 if ($self->{data} eq '') { # no more data, EOF
331 52         84 $self->{ttype} = -1;
332 52         80 $self->{currenttoken} = '';
333             }
334             else {
335 763         1273 my $i1 = length($self->{data}) + 1; # distance to starting anchor
336 763         979 my $i2 = $i1; # distance to ending anchor
337 763         981 my $pl=0; my $sl=0;
  763         929  
338 763         1067 $self->{ttype} = -2;
339 763         940 foreach my $ttype (0 .. $#{ $self->{hook} }) {
  763         1956  
340 5156         6849 my ($j, $pl2, $j2, $sl2); # $j dist to candidate starting achor
341             # $j2 dist to candidate ending anchor
342 5156 100       8584 if (exists($self->{hook}->[$ttype]->{'begin'})) {
343             # $j - to the start of the candidate active snippet
344 2984         5421 ($j,$pl2) = _index($self->{data}, $self->{hook}->[$ttype]->{'begin'});
345 2984 100 100     7621 next unless $j != -1 && $j <= $i1;
346 254         1060 my $data2 = substr($self->{data}, $j);
347 254 50       527 if ($self->{hook}->[$ttype]->{'end'} ne '') {
348 254         510 ($j2, $sl2) = _index($data2, $self->{hook}->[$ttype]->{'end'});
349 254 100       519 next if -1 == $j2;
350 246         359 $j2 += $j;
351 0         0 } else { $j2 = length($self->{data}) + 1; $sl2 = 0; }
  0         0  
352 246 100 100     508 next if ($j==$i1 and $i2<=$j2);
353 238         310 $i1 = $j; $pl = $pl2; $i2 = $j2; $sl = $sl2;
  238         312  
  238         298  
  238         316  
354 238         343 $self->{ttype} = $ttype;
355 238         511 $self->{args} = [];
356             } else { # matching regex hook
357 2172         9799 my @args = ($self->{data} =~ /$self->{hook}->[$ttype]->{regex}/m);
358 2172 100       4635 next unless @args;
359 981         1829 my $j = length($`);
360 981 100       2049 next unless $j < $i1;
361 551         664 $i1 = $j; $i2 = $i1+length($&); $sl=$pl=0;
  551         882  
  551         724  
362 551         1123 unshift @args, $&;
363 551         761 $self->{ttype} = $ttype;
364 551         1211 $self->{args} = \@args;
365             }
366             }
367            
368 763 100       1582 if ($self->{ttype}==-2) {
369 46         139 $self->{currenttoken}=$self->{data}; $self->{data}='' }
  46         101  
370             else { # live code
371 717 100       919 if (@{$self->{args}}) {
  717         1311  
372             #$self->{Out} = $newOut;
373 534         1624 $self->_process_outer( substr($self->{data}, 0, $i1) );
374             #$self->{Out} .= substr($self->{data}, 0, $i1);
375             ##$self->{data} = $newData;
376 534         1002 $self->{prefix} = '';
377 534         1074 $self->{currenttoken} = substr($self->{data}, $i1+$pl, $i2-$i1-$pl);
378 534         776 $self->{suffix} = '';
379 534         2010 $self->{data} = substr($self->{data}, $i2+$sl);
380             }
381             else {
382             # just copy type -2
383             # instead of returning as earlier, to
384             # support negative look-back for prefix
385             # $self->{Out} .= substr($self->{data}, 0, $i1);
386 183         665 $self->_process_outer( substr($self->{data}, 0, $i1) );
387 183         520 $self->{prefix} = substr($self->{data}, $i1, $pl);
388 183         577 $self->{currenttoken} = substr($self->{data}, $i1+$pl, $i2-$i1-$pl);
389 183         327 $self->{suffix} = substr($self->{data}, $i2, $sl);
390 183         593 $self->{data} = substr($self->{data}, $i2+$sl);
391             }
392             # Remove old output if it is there:
393 717 50       1534 if (defined($self->{OutDelimiters})) {
394 717         966 my ($b1,$b2,$e1,$e2) = @{ $self->{OutDelimiters} };
  717         1514  
395 717 100       4714 if ($self->{data} =~ /^\Q$b1\E(\d*)\Q$b2\E.*?\Q$e1\E\1\Q$e2\E/s) {
396 26         103 $self->{data} = $'; }
397             }
398             }
399             }
400            
401 815         1908 return $self->{ttype};
402             }
403              
404             # eval wrapper for string code
405             sub eval1 {
406 268     268 0 434 my $self = shift;
407 268 100       460 my $code = shift; $code = '' unless defined $code;
  268         611  
408 268         422 my $comment = shift;
409 3 100   3   24 eval("package main; no strict; $code");
  3 100   3   5  
  3     3   88  
  3     3   22  
  3     3   6  
  3     1   154  
  3     1   962  
  3     1   33193  
  3     3   51  
  3     1   12  
  3     3   18  
  3     8   43  
  3     64   19  
  3     9   17  
  3     7   24  
  3     4   13  
  1     1   1  
  1     1   36  
  1     1   6  
  1     1   2  
  3     1   41  
  1     1   7  
  1     1   2  
  1     1   21  
  1     1   7  
  1     2   2  
  1     1   40  
  1     1   6  
  1     1   2  
  3     1   23  
  3     2   11  
  3     1   13  
  3     1   56  
  3     1   14  
  7     1   44  
  2     2   23  
  1     1   7  
  3     2   13  
  2     1   64  
  1     5   6  
  1     1   13  
  3     9   45  
  3     1   15  
  1     1   2  
  3     1   30  
  2     1   7  
  1     1   2  
  1     1   40  
  1     1   6  
  1     1   2  
  1     1   19  
  1     1   7  
  1     1   2  
  1     1   55  
  1     1   7  
  1     1   2  
  1     1   14  
  1     2   7  
  1     1   2  
  1     3   73  
  1     1   7  
  1     1   2  
  1     4   31  
  1     2   7  
  1     1   2  
  1     1   21  
  1     1   6  
  1     1   2  
  1     1   40  
  1     1   6  
  1     1   2  
  3     1   17  
  3     1   12  
  3     1   11  
  1     1   36  
  3     1   11  
  3     1   26  
  3     1   34  
  3     1   13  
  3     1   8  
  3     1   42  
  3     1   15  
  3     1   13  
  3     1   48  
  3     1   56  
  1     1   2  
  1     1   22  
  1     1   7  
  1     1   2  
  2     1   49  
  2     1   14  
  2     1   6  
  2     1   155  
  3     1   22  
  2     1   10  
  1     1   40  
  2     1   10  
  2     1   8  
  2     1   77  
  1     1   10  
  1     1   3  
  1     1   26  
  1     1   7  
  1     1   2  
  1     1   28  
  2     1   10  
  2     1   5  
  2     1   19  
  2     1   16  
  1     1   2  
  1     1   23  
  1     1   8  
  1     1   2  
  1     1   44  
  2     1   11  
  2     1   5  
  1     1   23  
  1     1   7  
  1     1   2  
  1     1   45  
  1     1   6  
  1     1   2  
  1     1   175  
  1     1   11  
  1     1   3  
  1     1   41  
  1     1   9  
  1     1   2  
  1     1   56  
  1     1   6  
  1     1   2  
  1     1   132  
  1     1   7  
  1     1   3  
  3     1   29  
  3     1   11  
  3     1   7  
  3     1   52  
  3     1   12  
  3     1   10  
  8     1   38  
  7     1   17  
  7     1   17  
  1     1   43  
  7     1   37  
  1     1   2  
  1     1   55  
  1     1   16  
  7     1   14  
  7     1   60  
  2     1   11  
  7     1   27  
  2     1   30  
  1     1   8  
  1     1   3  
  1     1   35  
  1     1   7  
  1     1   2  
  1     1   39  
  1     1   11  
  1     1   6  
  1     1   38  
  2     1   9  
  1     1   11  
  1     1   50  
  1     1   7  
  1     1   2  
  1     1   22  
  1     1   7  
  2     1   7  
  1     1   33  
  1     1   7  
  1     1   2  
  1     1   29  
  1     1   7  
  1     1   3  
  1     1   31  
  1     1   6  
  1     1   2  
  1     1   21  
  1     1   7  
  8     1   12  
  8     1   73  
  8     1   33  
  8     1   20  
  64     1   138  
  64     1   71  
  64     1   86  
  1     1   32  
  1     1   6  
  1     1   3  
  64     1   196  
  9     1   17  
  9     1   26  
  9     1   204  
  9     1   28  
  2     1   5  
  2     1   34  
  8     1   21  
  8     1   12  
  8     1   61  
  8     1   23  
  8     1   11  
  8     1   32  
  8     1   33  
  57     1   63  
  57     1   101  
  57     1   92  
  57     1   173  
  8     1   61  
  8     1   27  
  8     1   18  
  8     1   70  
  8     1   18  
  1     1   2  
  1     1   85  
  8     1   25  
  8     1   12  
  8     1   104  
  8     1   15  
  8     1   12  
  8     1   34  
  8     1   22  
  1     1   3  
  1     1   42  
  1     1   6  
  1     1   6  
  1     1   18  
  1     1   6  
  1     1   2  
  1     1   45  
  1     1   12  
  1     1   2  
  8     1   39  
  2     1   10  
  2     1   8  
  7     1   46  
  7     1   19  
  1     1   2  
  1     1   44  
  1     1   6  
  1     1   3  
  1     1   23  
  7     1   31  
  7     1   18  
  7     1   60  
  7     1   19  
  7     1   14  
  7     1   33  
  7     1   17  
  7     1   27  
  7     1   116  
  1     1   7  
  9     1   21  
  7     1   43  
  7     1   15  
  7     1   18  
  7     1   45  
  7     1   414  
  7     1   32  
  1     1   32  
  1     1   7  
  1     1   3  
  1     1   21  
  1     1   45  
  4     1   8  
  4     1   53  
  4     1   12  
  4     1   6  
  4     6   43  
  4     2   27  
  2     15   4  
  2     2   25  
  2         9  
  4         9  
  4         46  
  4         27  
  4         15  
  3         26  
  2         11  
  1         1  
  2         60  
  1         6  
  1         3  
  1         15  
  1         6  
  1         4  
  1         104  
  1         7  
  1         2  
  1         31  
  1         9  
  1         2  
  1         36  
  1         6  
  1         2  
  1         29  
  1         7  
  1         2  
  1         35  
  1         6  
  1         2  
  1         28  
  1         6  
  1         3  
  1         34  
  1         6  
  1         3  
  1         27  
  1         7  
  1         2  
  1         35  
  1         7  
  1         3  
  1         25  
  1         7  
  1         2  
  1         36  
  1         6  
  1         4  
  1         25  
  1         6  
  1         3  
  1         34  
  1         6  
  1         2  
  1         25  
  1         7  
  1         2  
  1         21  
  1         7  
  1         2  
  1         41  
  1         6  
  1         2  
  1         51  
  1         22  
  1         3  
  1         39  
  1         6  
  1         143  
  1         48  
  1         7  
  1         2  
  1         21  
  1         6  
  1         3  
  1         40  
  1         7  
  1         2  
  1         39  
  1         6  
  1         2  
  1         49  
  1         6  
  1         2  
  1         37  
  1         7  
  1         2  
  1         21  
  1         7  
  1         4  
  1         45  
  1         7  
  1         5  
  1         58  
  1         7  
  1         2  
  1         22  
  1         7  
  1         2  
  1         18  
  1         6  
  1         3  
  1         37  
  1         7  
  1         2  
  1         130  
  1         7  
  1         2  
  1         17  
  1         38  
  1         3  
  1         50  
  1         7  
  1         2  
  1         21  
  1         7  
  1         2  
  1         38  
  1         5  
  1         2  
  1         122  
  1         7  
  1         2  
  1         52  
  1         7  
  1         2  
  1         41  
  1         7  
  1         2  
  1         39  
  1         6  
  1         2  
  1         49  
  1         7  
  1         2  
  1         26  
  1         7  
  2         4  
  2         50  
  2         8  
  2         4  
  2         24  
  2         11  
  1         2  
  2         44  
  2         12  
  2         9  
  1         103  
  2         10  
  2         7  
  2         70  
  1         7  
  1         6  
  1         49  
  1         7  
  1         2  
  1         58  
  1         7  
  1         2  
  1         21  
  1         7  
  1         2  
  1         16  
  1         8  
  1         3  
  1         40  
  1         6  
  1         2  
  1         22  
  1         7  
  1         3  
  1         38  
  1         6  
  1         2  
  1         115  
  1         7  
  1         2  
  1         37  
  1         7  
  1         3  
  1         37  
  1         7  
  1         2  
  1         36  
  1         6  
  1         3  
  1         18  
  1         7  
  1         2  
  1         15  
  1         7  
  1         2  
  1         39  
  1         6  
  2         4  
  2         35  
  2         10  
  2         3  
  2         39  
  2         11  
  2         6  
  2         166  
  1         12  
  1         2  
  1         38  
  1         5  
  1         2  
  1         48  
  1         8  
  1         2  
  1         36  
  1         6  
  1         1  
  1         34  
  1         7  
  1         5  
  1         16  
  1         7  
  1         2  
  1         42  
  1         7  
  1         2  
  1         159  
  1         6  
  1         3  
  1         23  
  1         8  
  1         2  
  1         44  
  1         7  
  1         2  
  1         22  
  1         6  
  1         3  
  1         47  
  1         6  
  1         2  
  1         122  
  1         7  
  1         2  
  1         21  
  1         7  
  2         4  
  2         46  
  2         8  
  2         5  
  2         62  
  2         11  
  2         6  
  2         26  
  2         10  
  2         3  
  1         46  
  1         7  
  1         2  
  1         21  
  1         9  
  1         2  
  2         42  
  2         18  
  2         5  
  1         20  
  1         8  
  1         2  
  1         26  
  5         16  
  5         27  
  21         193  
  5         21  
  1         3  
  9         145  
  9         31  
  1         2  
  1         48  
  9         31  
  5         25  
  1         40  
  5         13  
  5         28  
  1         28  
  5         15  
  5         33  
  2         54  
  1         7  
  2         7  
  1         44  
  1         7  
  1         6  
  3         53  
  5         16  
  5         10  
  5         63  
  1         6  
  5         22  
  1         39  
  5         20  
  5         12  
  5         50  
  5         19  
  5         35  
  5         33  
  5         21  
  5         38  
  1         25  
  1         6  
  1         3  
  1         25  
  1         7  
  1         2  
  2         162  
  2         10  
  2         9  
  2         128  
  1         7  
  1         2  
  1         46  
  1         6  
  1         3  
  1         36  
  1         8  
  1         2  
  1         29  
  2         10  
  2         7  
  2         57  
  2         9  
  1         2  
  1         42  
  1         7  
  1         2  
  1         41  
  5         22  
  1         1  
  1         42  
  1         6  
  1         2  
  1         40  
  1         6  
  1         3  
  1         39  
  1         8  
  1         1  
  1         25  
  1         8  
  1         2  
  1         26  
  1         8  
  1         2  
  1         22  
  1         7  
  1         3  
  1         26  
  1         8  
  1         2  
  1         46  
  1         9  
  1         2  
  1         21  
  1         7  
  1         2  
  1         42  
  1         6  
  1         3  
  1         20  
  1         7  
  1         2  
  1         19  
  1         7  
  1         2  
  1         40  
  1         6  
  1         2  
  1         22  
  1         7  
  1         2  
  1         43  
  1         7  
  1         2  
  1         20  
  1         24  
  1         10  
  1         44  
  1         14  
  1         14  
  1         67  
  1         6  
  1         7  
  1         53  
  1         14  
  1         7  
  1         58  
  1         7  
  1         3  
  1         34  
  1         8  
  1         2  
  1         42  
  1         6  
  1         3  
  1         53  
  1         17  
  1         7  
  1         36  
  1         8  
  1         2  
  1         48  
  1         6  
  1         2  
  1         22  
  1         15  
  1         3  
  1         39  
  1         6  
  1         8  
  1         48  
  1         6  
  1         2  
  1         20  
  1         7  
  1         3  
  1         54  
  1         6  
  1         9  
  1         26  
  1         52  
  1         25  
  1         99  
  1         25  
  1         7  
  1         149  
  1         26  
  1         23  
  1         184  
  1         21  
  1         12  
  1         71  
  1         15  
  1         9  
  1         257  
  1         8  
  1         3  
  1         30  
  1         6  
  1         10  
  1         54  
  1         7  
  1         6  
  1         30  
  1         6  
  1         22  
  1         53  
  2         39  
  2         16  
  2         141  
  2         18  
  2         10  
  2         83  
  2         19  
  2         18  
  2         39  
  2         16  
  2         8  
  2         68  
  2         9  
  2         12  
  2         36  
  2         15  
  2         37  
  2         61  
  2         11  
  2         10  
  2         30  
  2         11  
  2         6  
  2         40  
  2         15  
  2         13  
  2         25  
  2         19  
  2         10  
  2         49  
  2         10  
  2         6  
  2         60  
  2         25  
  1         7  
  1         33  
  1         7  
  1         10  
  1         44  
  1         7  
  1         14  
  1         45  
  1         15  
  3         49  
  1         45  
  1         6  
  1         3  
  1         31  
  1         7  
  1         3  
  1         32  
  1         8  
  1         2  
  1         71  
  1         8  
  1         2  
  1         64  
  4         15  
  4         16  
  4         145  
  4         127  
  4         35  
  4         92  
  2         10  
  2         7  
  2         352  
  2         14  
  2         6  
  2         42  
  2         141  
  1         3  
  1         41  
  1         7  
  1         2  
  1         171  
  1         8  
  1         3  
  1         23  
  1         18  
  1         2  
  1         45  
  1         7  
  1         2  
  1         174  
  268         18502  
  6         17  
  6         9  
  6         16  
  6         17  
  0         0  
  6         22  
  2         4  
  2         9  
  15         32  
  15         24  
  15         37  
  15         63  
  2         7  
  2         4  
  2         4  
  2         2  
  2         4  
  2         4  
  2         7  
  0         0  
  2         13  
  2         10  
410 268 50       1091 if ($@) {
411 0         0 my ($code1, $linecnt);
412 0         0 foreach (split(/\n/, $code))
413 0         0 { ++$linecnt; $code1 .= sprintf("%03d %s\n", $linecnt, $_); }
  0         0  
414 0         0 _croak("$comment code error:$@\ncode:\n$code1");
415             }
416             }
417              
418             # The main subroutine for evaluating a snippet of string
419             sub evaluate {
420 126     126 0 184 my $self = shift;
421              
422 126         192 my $pref = shift;
423 126         202 my $code = shift; my $c = $code;
  126         205  
424 126         203 my $suf = shift;
425 126 100 66     516 if (defined($self->{CodePreparation}) && $self->{CodePreparation}) {
426 83         167 local $_=$code;
427 83         212 $self->eval1($self->{CodePreparation},'preprocessing');
428 83         202 $code = $_;
429             }
430              
431             # Evaluate code, first final preparation and then eval1
432 126         318 local $::Star = $self;
433 126         283 local $::O = '';
434 126         359 $self->eval1($code, 'snippet');
435            
436 126 100       342 if ($self->{REPLACE}) { return $::O }
  61         216  
437 65 100       149 if ($::O ne '') { $suf.= $self->_output_wrap($::O); }
  31         99  
438 65         311 return "$pref$c$suf";
439             }
440              
441             # Wrap output with output delimiters
442             sub _output_wrap {
443 34     34   60 my $self = shift; my $out = shift; my @d = ("#","+\n","#","-");
  34         73  
  34         131  
444 34 50       82 @d = @{ $self->{OutDelimiters} } if defined( $self->{OutDelimiters} );
  34         111  
445 34         131 my ($b,$e) = ($d[0].$d[1], $d[2].$d[3]); my $i;
  34         46  
446 34 100       104 if (index($out, $e) != -1) {
447 6 100       12 while(1) { $i++; $e=$d[2].$i.$d[3]; last if index($out, $e)==-1;
  12         17  
  12         23  
  12         28  
448 6 50       12 _croak("Problem finding ending delimiter!\n(O=$out)") if $i > 1000000;}
449 6         12 $b = $d[0].$i.$d[1]; }
450 34         106 return $b.$out.$e; }
451              
452             # Python-specific evaluator (used also for makefile style)
453             sub evaluate_py {
454 0     0 0 0 my $self = shift;
455              
456 0         0 my $pref = shift;
457 0         0 my $code = shift; my $c = $code;
  0         0  
458 0         0 my $suf = shift;
459 0 0 0     0 if (defined($self->{CodePreparation}) && $self->{CodePreparation}) {
460 0         0 local $_=$code;
461 0         0 $self->eval1($self->{CodePreparation},'preprocessing');
462 0         0 $code = $_;
463             }
464              
465             # Evaluate code, first final preparation and then eval1
466 0         0 local $::Star = $self;
467 0         0 local $::O = '';
468 0         0 $self->eval1($code, 'snippet');
469              
470 0 0       0 if ($self->{REPLACE}) { return $::O }
  0         0  
471 0 0       0 if ($::O ne '') {
472 0         0 my $indent = '';
473 0 0       0 if ($pref =~ /^(\s+)#/) { $indent = $1 }
  0 0       0  
474 0         0 elsif ($c =~ /^(\s+)#/m) { $indent = $1 }
475 0         0 my $out = $::O;
476             # Avoiding extra \n before #-
477 0 0       0 $out.="\n" unless rindex($out, "\n")==length($out)-1;
478 0         0 $out.= $indent.$self->_output_wrap($out);
479 0         0 $suf.= $indent.$out;
480             }
481 0         0 return "$pref$c$suf";
482             }
483              
484             # Python-specific evaluator (used also for makefile style)
485             sub evaluate_py1 {
486 8     8 0 17 my $self = shift;
487 8         18 my $allmatch = shift;
488 8         21 my $indent = shift;
489 8         18 my $prefix = shift;
490 8         19 my $code = shift; my $c = $code;
  8         11  
491 8         15 my $oldout = shift;
492              
493 8 50 33     51 if (defined($self->{CodePreparation}) && $self->{CodePreparation}) {
494 8         20 local $_=$code;
495 8         30 $self->eval1($self->{CodePreparation},'preprocessing');
496 8         20 $code = $_;
497             }
498              
499             # Evaluate code, first final preparation and then eval1
500 8         22 local $::O = '';
501 8         14 local $::Star = $self;
502 8         23 $self->eval1($code, 'snippet');
503              
504 8 50       35 if ($self->{REPLACE}) { return $indent.$::O }
  0 100       0  
505 1         8 elsif ($::O eq '') { return "$indent#$prefix$c!>" }
506             else {
507 7         68 $::O =~ s/^/$indent/gmx;
508 7         15 my $r;
509 7         12 my ($b,$e); my @d = @{ $self->{OutDelimiters} };
  7         11  
  7         29  
510 7         17 $b = $d[0].$d[1]; my $i; $e = $d[2].$d[3];
  7         14  
  7         11  
511 7 50       33 if (index($::O, $e) != -1) {
512 0 0       0 while(1) { $i++; $e=$d[2].$i.$d[3]; last if index($::O, $e)==-1;
  0         0  
  0         0  
  0         0  
513 0 0       0 _croak("Problem finding ending delimiter!\n(O=$::O)")
514             if $i > 1000000; }
515 0         0 $b = $d[0].$i.$d[1];
516             }
517 7         28 $r= "$indent#$prefix$c!>$b".$::O;
518 7         55 $r =~ s/\n?$/\n/; $r.="$indent$e"; # no extra \n
  7         49  
519             }
520             }
521              
522             # predefined evaluator: echo
523             sub eval_echo {
524 1     1 0 5 my $self = shift;
525 1         2 my $pref = shift;
526 1         2 my $cont = shift;
527 1         2 my $suff = shift;
528 1         3 $::O = $cont;
529              
530             # to update OutDelimiters
531 1 50       4 return $::O if $self->{REPLACE};
532 1 50       4 return $pref.$cont.$suff if $::O eq '';
533 1         4 $suff.=$self->_output_wrap($::O);
534 1         4 return $pref.$cont.$suff;
535             }
536              
537             # predefined evaluator: ignore
538             sub eval_ignore {
539 2     2 0 15 my $self = shift;
540 2 100       15 return '' if $self->{REPLACE};
541              
542 1         3 my $pref = shift;
543 1         2 my $code = shift;
544 1         3 my $suf = shift;
545 1         4 return $pref.$code.$suf;
546             }
547              
548             # predefined ignore evaluator for regex hooks
549             sub repl_comment {
550 164     164 0 241 my $self = shift;
551 164 50       311 if ($self->{REPLACE}) { return '' }
  164         327  
552 0         0 return $self->{currenttoken};
553             }
554              
555             sub define {
556 1     1 0 2 my $self = shift;
557              
558 1         2 my $pref = shift;
559 1         2 my $data = shift;
560 1         3 my $suf = shift;
561              
562 1 50       4 if ($self->{CurrentLoop} > 1) { return "$pref$data$suf"; }
  0         0  
563              
564 1 50       6 $data =~ /^.+/ or _croak("expected macro spec");
565 1 50       5 _croak("no macro spec") unless $&;
566 1 50       16 _croak("double macro def (forbidden):$&") if ($self->{ForbidMacro}->{$&});
567 1         5 $self->{Macros}->{$&} = $data;
568 1         4 return '';
569             }
570              
571             sub MCdefine {
572 2     2 0 6 my $self = shift;
573 2         3 my $pref = shift;
574 2         4 my $data = shift;
575 2         5 my $suf = shift;
576              
577 2 50       6 if ($self->{CurrentLoop} > 1) { die "define in loop > 1 !?" }
  0         0  
578              
579 2 50       10 $data =~ /^.+/ or die "expected macro spec";
580 2 50       6 die "no macro spec" unless $&;
581 2 50       6 die "double macro def (forbidden):$&" if ($self->{ForbidMacro}->{$&});
582 2         13 $self->{Macros}->{$&} = $data;
583 2         13 return '';
584             }
585              
586             sub MCdefe {
587 2     2 0 12 my $self = shift;
588 2 50       8 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
589              
590 2         4 my $pref = shift;
591 2         4 my $data = shift;
592 2         4 my $suf = shift;
593              
594 2 50       6 if ($self->{CurrentLoop} > 1) { die "defe in a loop >1!?" }
  0         0  
595              
596 2 50       14 $data =~ /^.+/ or die "expected macro spec";
597 2 50       8 die "no macro spec" unless $&;
598 2 50       6 die "def macro forbidden:$&\n" if (defined $self->{ForbidMacro}->{$&});
599 2         11 $self->{Macros}->{$&} = $data;
600 2         9 return $self->{MacroKey}->{'expand'}.$&.$self->{MacroKey}->{'/expand'};
601             }
602              
603             sub MCnewdefe {
604 2 50   2 0 4 my $self = shift; die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
  2         16  
605              
606 2         4 my $pref = shift;
607 2         3 my $data = shift;
608 2         12 my $suf = shift;
609              
610 2 50       17 if ($self->{CurrentLoop} > 1) { die "newdefe in second loop!?" }
  0         0  
611              
612 2 50       11 $data =~ /^.+/ or die "expected macro spec";
613 2 50       6 die "no macro spec" unless $&;
614 2 50 33     23 if (defined $self->{Macros}->{$&} || $self->{ForbidMacro}->{$&}) {
615 0         0 die "double def:$&" }
616 2         8 $self->{Macros}->{$&} = $data;
617 2         5 $self->{ForbidMacro}->{$&} = 1;
618 2         15 return $self->{MprefExpand}.$&.$self->{MsufExpand};
619             }
620              
621             sub expand {
622 8     8 0 18 my $self = shift;
623 8 50       28 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
624              
625 8         15 my $pref = shift;
626 8         13 my $data = shift;
627 8         16 my $suf = shift;
628              
629 8 100 66     41 if ($self->{CurrentLoop} < 2 || $self->{HideMacros})
630 4         20 { return $self->{MacroKey}->{'expand'}.$data.$self->{MacroKey}->{'/expand'} }
631            
632 4 50       23 $data =~ /^.+/ or die "expected macro spec";
633 4 50       11 die "no macro spec" unless $&;
634             return $self->{MacroKey}->{'expanded'}.$self->{Macros}->{$&}.
635 4         22 $self->{MacroKey}->{'/expanded'};
636             }
637              
638             sub MCexpand {
639 14     14 0 24 my $self = shift;
640 14 50       37 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
641              
642 14         23 my $pref = shift;
643 14         23 my $data = shift;
644 14         20 my $suf = shift;
645              
646 14 100 66     56 if ($self->{CurrentLoop} < 2 || $self->{HideMacros}) {
647 2         13 return "$pref$data$suf"; }
648            
649 14 50       88 $data =~ /^.+/ or die "expected macro spec";
650 14 50       29 die "no macro spec" unless $&;
651 14 50       35 die "macro not defined" unless defined $self->{Macros}->{$&};
652 14         43 return $self->{MprefExpanded}.$self->{Macros}->{$&}.$self->{MsufExpanded};
653             }
654              
655             sub fexpand {
656 0     0 0 0 my $self = shift;
657 0 0       0 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
658              
659 0         0 my $pref = shift;
660 0         0 my $data = shift;
661 0         0 my $suf = shift;
662              
663 0 0       0 if ($self->{CurrentLoop} < 2) { return "$pref$data$suf"; }
  0         0  
664            
665 0 0       0 $data =~ /^.+/ or die "expected macro spec";
666 0 0       0 die "no macro spec" unless $&;
667 0 0       0 die "macro not defined:$&" unless defined $self->{Macros}->{$&};
668 0         0 return $self->{MpreffExpanded} . $self->{Macros}->{$&}.$self->{MsuffExpanded};
669             }
670              
671             sub MCfexpand {
672 0     0 0 0 my $self = shift;
673 0 0       0 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
674              
675 0         0 my $pref = shift;
676 0         0 my $data = shift;
677 0         0 my $suf = shift;
678              
679 0 0       0 if ($self->{CurrentLoop} < 2) { return "$pref$data$suf"; }
  0         0  
680            
681 0 0       0 $data =~ /^.+/ or die "expected macro spec";
682 0 50       0 die "no macro spec" unless $&;
683 0 50       0 die "macro not defined:$&" unless defined $self->{Macros}->{$&};
684             return $self->{MacroKey}->{'fexpanded'}.$self->{Macros}->{$&}.
685 0         0 $self->{MacroKey}->{'/fexpanded'};
686             }
687              
688             sub expanded {
689 2     2 0 5 my $self = shift;
690 2 50       7 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
691              
692 2         4 my $pref = shift;
693 2         3 my $data = shift;
694 2         3 my $suf = shift;
695            
696 2 50       10 $data =~ /^.+/ or die "expected macro name";
697 2 50       7 die "no macro spec" unless $&;
698 2         9 return $self->{MprefExpand}.$&.$self->{MsufExpand};
699             }
700              
701             sub MCexpanded {
702 4     4 0 5 my $self = shift;
703 4 50       21 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
704              
705 4         8 my $pref = shift;
706 4         7 my $data = shift;
707 4         5 my $suf = shift;
708            
709 4 50       21 $data =~ /^.+/ or die "expected macro name";
710 4 50       17 die "no macro spec" unless $&;
711 4         20 return $self->{MacroKey}->{'expand'}.$&.$self->{MacroKey}->{'/expand'};
712             }
713              
714             sub fexpanded {
715 0     0 0 0 my $self = shift;
716 0 0       0 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
717              
718 0         0 my $pref = shift;
719 0         0 my $data = shift;
720 0         0 my $suf = shift;
721            
722 0 0       0 $data =~ /^.+/ or die "expected macro name";
723 0 0       0 die "no macro spec" unless $&;
724 0         0 return $self->{MpreffExpand}.$&.$self->{MsuffExpand};
725             }
726              
727             sub MCfexpanded {
728 0     0 0 0 my $self = shift;
729 0 0       0 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
730              
731 0         0 my $pref = shift;
732 0         0 my $data = shift;
733 0         0 my $suf = shift;
734            
735 0 0       0 if ($self->{CurrentLoop} < 2) { return "$pref$data$suf"; }
  0         0  
736 0 0       0 $data =~ /^.+/ or die "expected macro name";
737 0 0       0 die "no macro spec" unless $&;
738 0 0       0 die "Macro not defined:$&" unless defined $self->{Macros}->{$&};
739             return $self->{MacroKey}->{'fexpanded'}.$self->{Macros}->{$&}.
740 0         0 $self->{MacroKey}->{'/fexpanded'};
741             }
742              
743             sub MCauxdefine {
744 0     0 0 0 my $self = shift;
745 0 0       0 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
746              
747 0         0 my $pref = shift;
748 0         0 my $data = shift;
749 0         0 my $suf = shift;
750            
751 0 0       0 $data =~ /^.+/ or die "expected macro name";
752 0 0       0 die "no macro spec" unless $&;
753 0         0 my $mn = $&;
754 0         0 $data = unwrap($data);
755 0 50       0 die "double macro def (forbidden):$mn\n" if ($self->{ForbidMacro}->{$mn});
756 0 50       0 if (! defined($self->{Macros}->{$mn}) ) { $self->{Macros}->{$mn}=$data }
  0         0  
757 0         0 return '';
758             }
759              
760             sub auxDefine {
761 3     3 0 8 my $self = shift;
762 3 50       14 die "(".ref($self).")" unless ref($self) eq 'Text::Starfish';
763              
764 3         7 my $pref = shift;
765 3         4 my $data = shift;
766 3         7 my $suf = shift;
767            
768 3 100       16 $data =~ /^.+/ or die "expected macro name";
769 3 50       11 die "no macro spec" unless $&; my $mn = $&;
  3         10  
770 3         10 $data = unwrap($data);
771 3 50       16 die "double macro def (forbidden):$mn\n" if ($self->{ForbidMacro}->{$mn});
772 3 100       11 if (! defined($self->{Macros}->{$mn}) ) { $self->{Macros}->{$mn}=$data }
  3         7  
773 3         13 return '';
774             }
775              
776             sub wrap {
777 10     10 0 18 my $d = shift;
778 10         102 $d =~ s/^/\/\/ /mg;
779 10         40 return $d;
780             }
781              
782             sub unwrap {
783 3     3 0 6 my $d = shift;
784 3         28 $d =~ s/^\/\/ //mg;
785 3         9 return $d;
786             }
787              
788             sub setGlobStyle {
789 0     0 0 0 my $self = shift;
790 0         0 my $s = shift;
791 0         0 $self->{STYLE} = $s;
792 0         0 $self->set_style($s);
793             }
794              
795             sub clearStyle {
796 75     75 0 115 my $self = shift;
797 75         178 foreach my $k (qw(Style CodePreparation LineComment OutDelimiters
798             IgnoreOuter)) {
799 375         699 delete $self->{$k} }
800 75         310 $self->{hook} = [];
801             }
802              
803 3     3 1 23 sub setStyle { return &set_style }
804              
805             # List of fields typically set in set_style:
806             # $self->{Style} = string, style
807             # $self->{CodePreparation} = scalar
808             # $self->{LineComment} = string, line comment
809             # $self->{OutDelimiters} = [] eg: "//" "+\n" "//" "-\n"
810             # $self->{IgnoreOuter} = 1 or ''
811             # $self->{hook} = [], list of hooks
812             sub set_style {
813 181     181 1 312 my $self = shift;
814 181 50       465 if (ref($self) ne 'Text::Starfish') { unshift @_, $self; $self = $::Star; }
  0         0  
  0         0  
815              
816 181 100       411 if ($#_ == -1) {
817 89 50 33     276 if (defined $self->{STYLE} && $self->{STYLE} ne '')
818 0         0 { $self->set_style($self->{STYLE}) }
819             else {
820 89         159 my $f = $self->{INFILE};
821              
822 89 100       335 if ($f =~ /\.(html\.sfish|sf)$/i) { $self->set_style('html.sfish') }
  2         7  
823             else {
824 87         169 $f =~ s/\.s(tar)?fish$//;
825 87 100       576 if ($f =~ /\.html?/i) { $self->set_style('html') }
  6 100       27  
    100          
    100          
    50          
    100          
826 5         19 elsif ($f =~ /\.(?:la)?tex$/i) { $self->set_style('tex') }
827 12         31 elsif ($f =~ /\.java$/i) { $self->set_style('java') }
828 3         10 elsif ($f =~ /^[Mm]akefile/) { $self->set_style('makefile') }
829 0         0 elsif ($f =~ /\.ps$/i) { $self->set_style('ps') }
830 1         4 elsif ($f =~ /\.py$/i) { $self->set_style('python') }
831 60         170 else { $self->set_style('perl') }
832             }
833             }
834 89         172 return;
835             }
836              
837 92         211 my $s = shift;
838 92 100 66     383 if ($s eq 'latex' or $s eq 'TeX') { $s = 'tex' }
  3         8  
839 92 100 100     340 if (defined $self->{Style} and $s eq $self->{Style}) { return }
  17         52  
840            
841             # default
842 75         195 $self->clearStyle();
843 75         184 $self->{'LineComment'} = '#';
844 75         143 $self->{'IgnoreOuter'} = '';
845 75         263 $self->{OutDelimiters} = [ "#", "+\n", "#", "-" ];
846             $self->{hook}= [
847 75         670 {ht=>'be', begin => '# '!>', f => \&evaluate },
848             {ht=>'be', begin => ' '!>', f => \&evaluate },
849             {ht=>'be', begin => ' '?>', f => \&evaluate }
850             ];
851 75         182 $self->{CodePreparation} = 's/\\n(?:#|%|\/\/+)/\\n/g';
852              
853             # Used for Python and Makefile with &evaluate_py1
854 75         428 my $re_py1 = qr/([\ \t]*)\#(\ *<\?)([\000-\377]*?)!>/x;
855             # extension below was a bug for ...#+...#-
856             # ([\ \t]*\#+\n[\000-\377]*?\n[\ \t]*\#-\n)?/x;
857              
858 75 100       359 if ($s eq 'perl') { }
    100          
    100          
    100          
    100          
    100          
    50          
    0          
859             elsif ($s eq 'makefile') {
860 3         13 $self->{hook} = [ ];
861 3         13 $self->addHook($re_py1, \&evaluate_py1);
862 3         5 $self->{CodePreparation} = 's/\\n\\s*#/\\n/g';
863             }
864             elsif ($s eq 'python') {
865 1         9 $self->{hook} = [ ];
866 1         25 $self->addHook($re_py1, \&evaluate_py1);
867 1         4 $self->{CodePreparation} = 's/\\n\\s*#/\\n/g';
868             }
869             elsif ($s eq 'java') {
870 12         26 $self->{LineComment} = '//';
871 12         35 $self->{OutDelimiters} = [ "//", "+\n", "//", "-" ];
872 12         65 $self->{hook} = [{begin => '// '!>', f => \&evaluate },
873             {begin => ' '!>', f => \&evaluate }];
874 12         27 $self->{CodePreparation} = 's/^\s*\/\/+//mg';
875             }
876             elsif ($s eq 'tex') {
877 7         30 $self->{LineComment} = '%';
878             # change OutDelimiters ?
879             # $self->{OutDelimiters} = [ "%", "+\n", "%", "-\n" ];
880 7         35 $self->{OutDelimiters} = [ "%", "+\n", "\n%", "-\n" ];
881 7         92 $self->{hook}=[{ht=>'be', begin => '% "!>\n", f => \&evaluate },
882             # change to this one?
883             #{ht=>'be', begin => '% "!>", f => \&evaluate },
884             {ht=>'be', begin => ' "!>\n", f => \&evaluate },
885             {ht=>'be', begin => ' "!>", f => \&evaluate }];
886              
887 7         25 $self->{CodePreparation} = 's/^[ \t]*%//mg';
888             }
889             elsif ($s eq 'html.sfish') {
890 2         6 undef $self->{LineComment};
891 2         10 $self->{OutDelimiters} = [ "", "" ];
892             $self->{hook}=[
893 2         23 {ht=>'be', begin=>'', f => \&evaluate },
894             {ht=>'be', begin=>''?>', f=>\&evaluate },
895             {begin=>qr/<\?sf\s/, end=>qr/!>/, f=>\&evaluate },
896             ];
897 2         15 $self->addHook(qr/^#.*\n/m, 'comment');
898 2         6 $self->{CodePreparation} = '';
899             }
900             elsif ($s eq 'html') {
901 6         23 undef $self->{LineComment}; # Changes
902 6         32 $self->{OutDelimiters} = [ "", "" ];
903             $self->{hook}=[
904 6         67 {ht=>'be', begin => '', f => \&evaluate },
905             {ht=>'be', begin=>''?>', f=>\&evaluate } ];
906 6         21 $self->{CodePreparation} = '';
907             }
908             elsif ($s eq 'ps') {
909 0         0 $self->{LineComment} = '%';
910 0         0 $self->{OutDelimiters} = [ "% ", "+\n", "% ", "-" ];
911             $self->{hook}=[
912 0         0 {ht=>'be', begin => ' '!>', f => \&evaluate }];
913 0         0 $self->{CodePreparation} = 's/\\n%/\\n/g';
914             }
915 0         0 else { _croak("set_style:unknown style:$s") }
916 75         282 $self->{Style} = $s;
917             }
918              
919             # to be deprecated? Used only to make it available in name space
920 6     6 1 21 sub sfish_add_tag($$) { $::Star->add_tag(@_) }
921 2     2 1 8 sub sfish_ignore_outer { $::Star->ignore_outer(@_) }
922              
923             # adds tags such as %slide:.* and % by adding appropriate hooks
924             # eg: add_tag('slide', 'ignore')
925             # eg: add_tag('sl,l', 'echo')
926             sub add_tag {
927 6     6 1 12 my $self = shift;
928 6 50       21 if (ref($self) ne 'Text::Starfish')
929 0         0 { unshift @_, $self; $self = $::Star; }
  0         0  
930 6         10 my $tag = shift; my $fun = shift;
  6         14  
931 6         25 my $lc = $self->{LineComment};
932             #die "tag=($tag) fun=($fun) ref(fun)=".ref($fun);
933 6 50       24 if (ref($fun) eq '') {
934 6 100   6   27 if ( $fun eq 'ignore') { $fun = sub{''} }
  1 50       6  
  6         13  
935 5     23   37 elsif ( $fun eq 'echo') { $fun = sub{$_[2]} }
  23         52  
936             }
937 6 50       24 my $lc1 = ($lc eq '') ? $lc : "$lc?";
938 6         167 $self->addHook(qr/$lc1<$tag>\n?((?:.|\n)*?)$lc1<\/$tag>\n?/, $fun);
939 6         81 $self->addHook(qr/$lc$tag:(.*\n)/, $fun);
940             }
941              
942             sub ignore_outer {
943 2     2 1 4 my $self = shift;
944 2 50       7 if (ref($self) ne 'Text::Starfish')
945 0         0 { unshift @_, $self; $self = $::Star; }
  0         0  
946 2         3 my $newignoreouter = 1;
947 2 50       6 $newignoreouter = $_[1] if $#_ > 0;
948 2         35 $self->{IgnoreOuter} = $newignoreouter;
949             }
950              
951             sub set_out_delimiters {
952 4     4 0 8 my $self = shift;
953 4 50       13 if (ref($self) ne 'Text::Starfish')
954 4         12 { unshift @_, $self; $self = $::Star; }
  4         6  
955 4 50       10 _croak("OutDelimiters must be array of 4 elements:(@_)") if scalar(@_)!=4;
956 4         113 $self->{OutDelimiters} = [ $_[0], $_[1], $_[2], $_[3] ]; }
957            
958             sub add_hook {
959 11     11 1 23 my $self = shift;
960 11 100       32 if (ref($self) ne 'Text::Starfish')
961 8         17 { unshift @_, $self; $self = $::Star; }
  8         12  
962              
963 11         20 my $ht = shift;
964 11         29 my $hooks = $self->{hook}; my $hook = { ht=>$ht };
  11         33  
965 11 100       32 if ($ht eq 'regex') {
    50          
966 7         10 my $regex=shift; my $replace = shift;
  7         9  
967 7         14 $hook->{regex} = $regex;
968 7 50 33     28 if (ref($replace) eq '' && $replace eq 'comment')
    50          
969 0         0 { $hook->{replace} = \&repl_comment }
970             elsif (ref($replace) eq 'CODE')
971 7         15 { $hook->{replace} = $replace }
972 0         0 else { _croak("add_hook, undefined regex format input ".
973             "(TODO?): ref regex(".ref($regex).
974             "), ref replace(".ref($replace).")" ) }
975 7         10 push @{$hooks}, $hook;
  7         99  
976             } elsif ($ht eq 'be') {
977 4         7 my $b = shift; my $e = shift; my $f='default';
  4         6  
  4         7  
978 4 50       10 if ($#_>-1) { $f = shift }
  0         0  
979 4         12 $self->addHook($b, $e, $f);
980 0         0 } else { _croak("add_hook error, unknown hook type `$ht'") }
981             }
982              
983             # addHook is deprecated. Use add_hook, which contains the hook type
984             # as the second argument, after $self.
985             sub addHook {
986 44     44 1 109 my $self = shift;
987 44         64 my @Hook = @{ $self->{hook} };
  44         139  
988              
989 44 100 33     221 if ($#_ == 2) {
    50          
990 20         38 my $p = shift; my $s=shift; my $fun=shift;
  20         34  
  20         46  
991 20 100       87 if ($fun eq 'default')
    100          
    100          
    100          
992 5         23 { push @Hook, {begin=>$p, end=>$s, f=>\&evaluate} }
993             elsif ($fun eq 'ignore')
994 2         11 { push @Hook, {begin=>$p, end=>$s, f=>\&eval_ignore} }
995             elsif ($fun eq 'echo')
996 1         5 { push @Hook, {begin=>$p, end=>$s, f=>\&eval_echo} }
997             elsif (ref($fun) eq 'CODE') {
998             push @Hook, {begin=>$p, end=>$s,
999 3     3   6 f=> sub { local $_; my $self=shift;
  3         8  
1000 3         4 my $p=shift; $_=shift; my $s=shift;
  3         6  
  3         5  
1001 3         65 &$fun($p,$_,$s);
1002 3 50       9 if ($self->{REPLACE}) { return $_ }
  3         7  
1003 0         0 return "$p$_$s";
1004             }
1005 1         6 };
1006             }
1007             else {
1008 11         2070 eval("push \@Hook, {begin=>\$p, end=>\$s,".
1009             "f=>sub{\n".
1010             "local \$_;\n".
1011             "my \$self = shift;\n".
1012             "my \$p = shift; \$_ = shift; my \$s = shift;\n".
1013             "$fun;\n".
1014             'if ($self->{REPLACE}) { return $_ }'."\n".
1015             "return \"\$p\$_\$s\"; } };");
1016             }
1017 20 50       82 _croak("addHook error:$@") if $@;
1018             } elsif ($#_ == 1 and ref($_[0]) eq 'Regexp') { #regex hook
1019 24         44 my $regex=shift; my $replace = shift;
  24         37  
1020 24 100 66     117 if (ref($replace) eq '' && $replace eq 'comment')
    50          
1021 4         18 { push @Hook, {regex=>$regex, replace=>\&repl_comment} }
1022             elsif (ref($replace) eq 'CODE')
1023 20         85 { push @Hook, {regex=>$regex, replace=>$replace} }
1024 0         0 else { _croak("addHook, undefined regex format input ".
1025             "(TODO?): ".ref($regex).", ".ref($replace)
1026             ) }
1027 0         0 } else { _croak("addHook parameter error") }
1028              
1029 44         560 $self->{hook} = \@Hook;
1030             }
1031              
1032             sub rm_hook {
1033 5     5 1 10 my $self = shift;
1034 5 100       17 if (ref($self) ne 'Text::Starfish')
1035 4         10 { unshift @_, $self; $self = $::Star; }
  4         9  
1036              
1037 5         7 my $ht = shift; # hook type: be (begin-end)
1038 5 50       16 if ($ht eq 'be') {
1039 5         9 my $b=shift; my $e=shift;
  5         8  
1040 5         25 my @Hooks = @{ $self->{hook} }; my @Hooks1;
  5         16  
  5         10  
1041 5         12 foreach my $h (@Hooks) {
1042 19 100 66     62 if ($h->{begin} eq $b and $h->{end} eq $e) {}
1043 14         25 else { push @Hooks1, $h }
1044             }
1045 5         101 $self->{hook} = \@Hooks1;
1046             } else {
1047 0         0 _croak("rm_hook not implemented for type ht=($ht)") }
1048             }
1049              
1050             # rmHook to be deprecated. Needs to be replaced with rm_hook
1051             sub rmHook {
1052 1     1 1 3 my $self = shift;
1053 1         2 my $p = shift;
1054 1         3 my $s = shift;
1055 1         4 $self->rm_hook('be', $p, $s); return; }
  1         8  
1056              
1057             sub rmAllHooks {
1058 0     0 1 0 my $self = shift;
1059 0         0 $self->{hook} = [];
1060             }
1061              
1062 0     0 0 0 sub resetHooks { my $self = shift; $self->rmAllHooks(); $self->set_style(); }
  0         0  
  0         0  
1063              
1064             sub add_final {
1065 2     2 1 11 my $self = shift;
1066 2 50       5 my $f = shift; die "$f not a function" unless ref($f) eq 'CODE';
  2         18  
1067 2 50       9 if (!defined($self->{Final})) { $self->{Final} = [] }
  2         6  
1068 2         6 push @{ $self->{Final} }, $f;
  2         21  
1069             }
1070              
1071             sub defineMacros {
1072 12     12 0 31 my $self = shift;
1073              
1074 12 100       73 return if $self->{CurrentLoop} > 1;
1075 6 50       21 $self->{Loops} = 2 if $self->{Loops} < 2;
1076 6         21 $self->{MprefDefine} = '//define ';
1077 6         14 $self->{MsufDefine} = "//enddefine\n";
1078 6         25 $self->{MprefExpand} = '//expand ';
1079 6         14 $self->{MsufExpand} = "\n";
1080 6         27 $self->{MacroKey}->{'expand'} = '//m!expand ';
1081 6         14 $self->{MacroKey}->{'/expand'} = "\n";
1082 6         16 $self->{MacroKey}->{'expanded'} = '//m!expanded ';
1083 6         17 $self->{MacroKey}->{'/expanded'} = "//m!end\n";
1084 6         16 $self->{MpreffExpand} = '//fexpand ';
1085 6         16 $self->{MsuffExpand} = "\n";
1086 6         22 $self->{MacroKey}->{'fexpand'} = '//m!fexpand ';
1087 6         12 $self->{MacroKey}->{'/fexpand'} = "\n";
1088 6         13 $self->{MprefExpanded} = '//expanded ';
1089 6         17 $self->{MsufExpanded} = "//endexpanded\n";
1090 6         11 $self->{MpreffExpanded} = '//fexpanded ';
1091 6         15 $self->{MsuffExpanded} = "//endexpanded\n";
1092 6         17 $self->{MacroKey}->{'fexpanded'} = '//m!fexpanded ';
1093 6         22 $self->{MacroKey}->{'/fexpanded'} = "//m!end\n";
1094 6         11 $self->{MprefAuxDefine}='//auxdefine ';
1095 6         17 $self->{MsufAuxDefine}="//endauxdefine\n";
1096 6         12 $self->{MacroKey}->{'auxdefine'}='//m!auxdefine ';
1097 6         9 $self->{MacroKey}->{'/auxdefine'}="//m!endauxdefine\n";
1098 6         16 $self->{MacroKey}->{'define'} = '//m!define ';
1099 6         12 $self->{MacroKey}->{'/define'} = "//m!end\n";
1100 6         13 $self->{MacroKey}->{'defe'} = '//m!defe ';
1101 6         9 $self->{MacroKey}->{'/defe'} = "//m!end\n";
1102 6         11 $self->{MacroKey}->{'newdefe'} = '//m!newdefe ';
1103 6         15 $self->{MacroKey}->{'/newdefe'} = "//m!end\n";
1104 6         192 push @{$self->{hook}},
1105             {begin=>$self->{MprefDefine}, end=>$self->{MsufDefine}, f=>\&define},
1106             {begin=>$self->{MprefExpand}, end=>$self->{MsufExpand}, f=>\&expand},
1107             {begin=>$self->{MpreffExpand}, end=>$self->{MsuffExpand}, f=>\&fexpand},
1108             {begin=>$self->{MprefExpanded}, end=>$self->{MsufExpanded}, f=>\&expanded},
1109             {begin=>$self->{MpreffExpanded},
1110             end=>$self->{MsuffExpanded},f=>\&fexpanded},
1111             {begin=>$self->{MprefAuxDefine},
1112             end=>$self->{MsufAuxDefine},f=>\&auxDefine},
1113             {begin=>$self->{MacroKey}->{'auxdefine'},
1114             end=>$self->{MacroKey}->{'/auxdefine'},f=>\&MCauxdefine},
1115             {begin=>$self->{MacroKey}->{'define'},
1116             end=>$self->{MacroKey}->{'/define'}, f=>\&MCdefine},
1117             {begin=>$self->{MacroKey}->{'expand'},
1118             end=>$self->{MacroKey}->{'/expand'}, f=>\&MCexpand},
1119             {begin=>$self->{MacroKey}->{'fexpand'},
1120             end=>$self->{MacroKey}->{'/fexpand'}, f=>\&MCfexpand},
1121             {begin=>$self->{MacroKey}->{'expanded'},
1122             end=>$self->{MacroKey}->{'/expanded'},f=>\&MCexpanded},
1123             {begin=>$self->{MacroKey}->{'fexpanded'},
1124             end=>$self->{MacroKey}->{'/fexpanded'},f=>\&MCfexpanded},
1125             {begin=>$self->{MacroKey}->{'defe'},
1126             end=>$self->{MacroKey}->{'/defe'}, f=>\&MCdefe},
1127             {begin=>$self->{MacroKey}->{'newdefe'},
1128 6         11 end=>$self->{MacroKey}->{'/newdefe'}, f=>\&MCnewdefe};
1129 6         56 $self->{macrosdefined} = 1;
1130             }
1131              
1132             sub getmakefilelist ($$) {
1133 1     1 1 7 my $f = getfile($_[0]); shift;
  1         15  
1134 1         3 my $l = shift;
1135 1 50       29 $f =~ /\b$l=(.*(?:(?<=\\)\n.*)*)/ or
1136             die "starfish:getmakefilelist:no list:$l";
1137 1         5 $f=$1; $f=~s/\\\n/ /g;
  1         7  
1138 1         4 $f =~ s/^\s+//; $f =~ s/\s+$//;
  1         5  
1139 1         11 return split(/\s+/, $f);
1140             }
1141              
1142             # Should be used for example
1143             # e.g. addHookUnComment("\n%Dbegin", "\n%Dend");
1144             #sub addHookUnComment {
1145             # my $t1 = shift;
1146             # my $t2 = shift;
1147             # addHook($t1, $t2, 's/\n%!/\n/g');
1148             #}
1149             #
1150             #sub addHookComment {
1151             # my $t1 = shift;
1152             # my $t2 = shift;
1153             # addHook($t1, $t2, 's/\n(?:%!)?/\n%!/g');
1154             #}
1155              
1156 64     64 1 699 sub echo(@) { $::O .= join('', @_) }
1157              
1158             # used in LaTeX mode to include verbatim textual files
1159             sub get_verbatim_file {
1160 0     0 1 0 my $f = shift;
1161 0         0 return "\\begin{verbatim}\n".
1162             untabify(scalar(getfile($f))).
1163             "\\end{verbatim}\n";
1164             }
1165              
1166             sub untabify {
1167 0     0 0 0 local $_ = shift;
1168 0         0 my ($r, $l);
1169 0         0 while (/[\t\n]/) {
1170 0 50       0 if ($& eq "\n") { $r.="$l$`\n"; $l=''; $_ = $'; }
  0         0  
  0         0  
  0         0  
1171             else {
1172 0         0 $l .= $`;
1173 0         0 $l .= ' ' x (8 - (length($l) & 7));
1174 0         0 $_ = $';
1175             }
1176             }
1177 0         0 return $r.$l.$_;
1178             }
1179              
1180             sub getfile($) {
1181 48     48 1 109 my $f = shift;
1182 48         121 local *F;
1183 48 50       1694 open(F, "<$f") or die "starfish:getfile:cannot open $f:$!";
1184 48         2252 my @r = ;
1185 48         520 close(F);
1186 48 50       876 return wantarray ? @r : join ('', @r);
1187             }
1188              
1189             sub putfile($@) {
1190 102     102 1 2082313 my $f = shift;
1191 102         362 local *F;
1192 102 50       5803 open(F, ">$f") or die "starfish:putfile:cannot open $f:$!";
1193 102 100       563 print F '' unless @_;
1194 102         255 while (@_) { print F shift(@_) }
  79         1091  
1195 102         4454 close(F);
1196             }
1197              
1198             sub appendfile($@) {
1199 0     0 1   my $f = shift;
1200 0           local *F;
1201 0 0         open(F, ">>$f") or die "starfish:appendfile:cannot open $f:$!";
1202 0 0         print F '' unless @_;
1203 0           while (@_) { print F shift(@_) }
  0            
1204 0           close(F);
1205             }
1206              
1207             sub htmlquote($) {
1208 0     0 1   local $_ = shift;
1209 0           s/&/&/g;
1210 0           s/
1211 0           s/\"/"/g;
1212 0           return $_;
1213             }
1214              
1215             sub read_records($ ) {
1216 0     0 1   my $arg = shift;
1217 0 0         if ($arg =~ /^file=/) {
1218 0 0         my $f = $'; local *F; open(F, $f) or croak "cannot open $f:$!";
  0            
  0            
1219 0           $arg = join('', );
1220 0           close(F);
1221             }
1222              
1223 0           my $db = [];
1224 0           while ($arg) {
1225 0 0         if ($arg =~ /^([ \t\r]*(#.*)?\n)+/) { $arg = $'; }
  0            
1226 0 0         last if $arg eq ''; my $record;
  0            
1227 0 0         if ($arg =~ /([ \t\r]*\n){2,}/) { $record = "$`\n"; $arg = $'; }
  0            
  0            
1228 0           else { $record = $arg; $arg = ''; }
  0            
1229 0           my $r = {}; my $recordsave = $record;
  0            
1230 0           while ($record) {
1231 0 0         if ($record =~ /^[ \t]*#.*\n/) { $record=$'; next; } # allow
  0            
  0            
1232             # comments in records
1233 0 0         $record =~ /^[ \t]*([^\n:]*?)[ \t]*:/ or
1234             croak "db8: no attribute in record: ($recordsave)";
1235 0           my $k = $1; $record = $'; my $v;
  0            
  0            
1236 0 0         croak "empty key in ($recordsave)" if $k eq '';
1237 0           while (1) { # .................... line continuation
1238 0 0         if ($record =~ /^(.*?)\\(\r?\n)/) { $v .= $1.$2; $record = $'; }
  0 0          
  0 0          
1239 0           elsif ($record =~ /^.*?\r?\n[ \t]/) { $v .= $&; $record = $'; }
  0            
1240 0           elsif ($record =~ /^(.*?)\r?\n/) { $v .= $1; $record = $'; last; }
  0            
  0            
1241 0           else { $v .= $record; $record = ''; last }
  0            
  0            
1242             }
1243 0 0         if (exists($r->{$k})) {
1244 0           my $c = 0;
1245 0           while (exists($r->{"$k-$c"})) { ++$c }
  0            
1246 0           $k = "$k-$c";
1247             }
1248 0           $r->{$k} = $v;
1249             }
1250 0           push @{ $db }, $r;
  0            
1251             }
1252 0 0         return wantarray ? @{$db} : $db;
  0            
1253             }
1254              
1255 0     0 1   sub current_year { return POSIX::strftime("%Y", localtime(time)) }
1256              
1257             sub last_update() {
1258 0 0   0 1   my $self = @_ ? shift : $::Star;
1259 0 0         if ($self->{Loops} < 2) { $self->{Loops} = 2 }
  0            
1260 0           return POSIX::strftime("%d-%b-%Y", localtime($self->{LastUpdateTime}));
1261             }
1262              
1263             sub file_modification_time() {
1264 0 0   0 1   my $self = @_ ? shift : $::Star;
1265 0           return (stat $self->{INFILE})[9];
1266             }
1267              
1268             sub file_modification_date() {
1269 0 0   0 1   my $self = @_ ? shift : $::Star;
1270              
1271 0           my $t = $self->file_modification_time();
1272 0           my @a = localtime($t); $a[5] += 1900;
  0            
1273 0           return qw/January February March April May June July
1274             August September October November December/
1275             [$a[4]]." $a[3], $a[5]";
1276             }
1277              
1278             sub read_starfish_conf() {
1279 0 0   0 1   return unless -e "starfish.conf";
1280 0           my @dirs = ( '.' );
1281 0           while ( -e "$dirs[0]/../starfish.conf" )
1282 0           { unshift @dirs, "$dirs[0]/.." }
1283              
1284 0           my $currdir = cwd();
1285 0           foreach my $d (@dirs) {
1286 0 0         chdir $d or die "cannot chdir to $d";
1287             package main;
1288 0           require "$currdir/$d/starfish.conf";
1289             package Text::Starfish;
1290 0 0         chdir $currdir or die "cannot chdir to $currdir";
1291             }
1292             }
1293              
1294             sub _croak {
1295 0     0     my $m = shift;
1296 0           require Carp;
1297 0           Carp::croak($m);
1298             }
1299              
1300             # used in makefile mode
1301             #kw:makefile
1302             sub make_add_dirs_to_generate_if_needed {
1303 0     0 0   for my $d (@_) {
1304 0 0         next if grep { $_ eq $d } @DirGenerateIfNeeded;
  0            
1305 0           push @DirGenerateIfNeeded, $d;
1306             } }
1307             sub make_gen_dirs_to_generate {
1308 0     0 0   foreach my $d (@DirGenerateIfNeeded) {
1309 0           echo "$d:; mkdir -p \$\@\n" } }
1310              
1311             1;
1312              
1313             __END__