index: release v1.18 with only altgr index linked
[sheet.git] / source.plp
1 <(common.inc.plp)><:
2
3 my $source = $Request;
4 my $incname = qr{ [a-z][/a-z0-9_.-]* \.(?:plp?|css|js|txt) }x;
5
6 if ($source =~ s{(?<=\Q.inc.pl\E)/jsonp?$}{} and -r $source) {
7         # convert perl include to json construct
8         checkmodified($source);
9         eval {
10                 my $data = do $source or die $@ || $! || 'read error';
11                 require JSON;
12                 my $converter = JSON->new;
13                 $converter->indent->space_after->canonical;
14
15                 $header{content_type} = 'application/json';
16                 $header{'Access-Control-Allow-Origin'} = '*';
17                 $header{content_type} = 'text/plain' if exists $get{debug};
18                 print $_, '(' for $get{callback} // ();
19                 print $converter->encode($data);
20                 print     ')' for $get{callback} // ();
21                 return 1;
22         } or do {
23                 $header{status} = '500 File unavailable';
24                 $header{content_type} = 'text/plain';
25                 print "Conversion failed: $@";
26         };
27         exit;
28 }
29
30 Html({
31         title => "$source source code",
32         version => '1.3',
33         description => !$source ? 'Index of source files for this site.' : [
34                 "Source code of the $source file at this site,",
35                 "with syntax highlighted and references linked."
36         ],
37         keywords => [qw'
38                 sheet cheat source code perl plp html agpl
39         '],
40         stylesheet => [qw'light dark mono red'],
41         data => [$source =~ m{\A($incname)\z}],
42 });
43
44 say '';
45
46 if (not $source or -d $source) {
47         PLP_START {
48                 print "<h1>Source files</h1>";
49         };
50
51         if ($source and $source ne 'tools') {
52                 Abort("Directory index not permitted", '403 source not allowed');
53         }
54
55         print "<p>Project code distributed under the AGPL. Please contribute back.</p>";
56         say '<ul>';
57         for (glob($source ? "$source/*" : '*.plp')) {
58                 say '<li>', showlink($_, "/source/$_");
59         }
60         say "</ul>\n";
61 }
62 else {
63         my $href = showlink($source, $source =~ m{\A (\w+) \.plp \z}x && "/$1");
64         PLP_START {
65                 say "<h1>Source of $href</h1>";
66         };
67
68         my $path = $source;
69         if ($source =~ m{(?:/|^)\.}) {
70                 Abort("File request not permitted", '403 source not allowed');
71         }
72         elsif ($source =~ s{::}{/}g or !-e $source) {
73                 $source .= '.pm';
74                 for (0 .. $#INC) {
75                         -e ($_ = "$INC[$_]/$source") or next;
76                         $path = $_;
77                         last;
78                 }
79         }
80         -r $path or Abort("Requested file not found", '404 source not found');
81         my $size = (stat $path)->[7];
82
83         my $cachefile = "source/$source.html";
84         if (-e $cachefile and (stat $cachefile)->[9] >= (stat $path)->[9]) {
85                 say '<pre>';
86                 print decode_utf8(ReadFile($cachefile));
87                 say '</pre>';
88                 exit;
89         }
90         -e or mkdir for $cachefile =~ s{[^/]+\z}{}r; # dirname
91         open my $cache, '>', $cachefile
92                 or Alert("Could not save cache", "Opening $cachefile failed: $!");;
93
94         if (my $hl = eval {
95                 $size < 32_768 or die 'large files take too long to parse';
96                 require Text::VimColor;
97                 Text::VimColor->VERSION(0.12)
98                         or die 'early versions are buggy under FastCGI';
99                 delete $Text::VimColor::SYNTAX_TYPE{Underlined};
100                 return Text::VimColor->new(
101                         file => $path,
102                         vim_options => [@Text::VimColor::VIM_OPTIONS,
103                                 '+:set enc=utf-8',
104                                 '+:let perl_sub_signatures=1',
105                         ],
106                 )->marked;
107         }) {
108                 my %TYPETAG = (
109                         Statement => 'strong',
110                         Error     => 'em',
111                         Todo      => 'em',
112                         PreProc   => 'strong',
113                 );
114
115                 say '<pre>';
116                 foreach (@{$hl}) {
117                         my ($type, $contents) = @{$_};
118                         $contents = decode_utf8($contents);
119                         my $tag = $type && ($TYPETAG{$type} || 'span');
120                         my $line = Text::VimColor::_xml_escape($contents);
121
122                         # link other page sources, stylesheets, and javascript
123                         $line =~ s{ ^(['"]?) \K ($incname) (?=\1$) }{ showlink($2, "/source/$2") }xe
124                                 if !$type || $type eq 'Constant';
125                         # link relative page locations in html output
126                         $line =~ s{ ^(&quot;)\K (/\w+) (?= (?:/\w+)* \1$) }{ showlink($2, "/source$2.plp") }xe
127                                 if $type && $type eq 'Constant';
128                         # link perl module names (Xx::Xx...)
129                         $line =~ s{ ^\s* \K ([A-Z]\w+(?:::\w+)+) (?![^;\s]) }{ showlink($1, "/source/$1") }xe
130                                 if !$type;
131                         # link generator scripts (by tools/...)
132                         $line =~ s{ ^.*? by\  \K (tools/\S+) }{ showlink($1, "/source/$1") }xe
133                                 if $type && $type eq 'Comment';
134
135                         $line = qq(<$tag class="sy-\l$type">$line</$tag>) if $tag;
136                         print $line;
137                         print {$cache} $line if $cache;
138                 }
139                 say '</pre>';
140         }
141         else {
142                 say '<pre>';
143                 print EscapeHTML(decode_utf8(ReadFile($path)));
144                 say '</pre>';
145         }
146
147         say '';
148 }
149