sc: lotv patch v5.0.13 (2024-03-26)
[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.4',
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         data => [$source =~ m{\A($incname)\z}],
41 });
42
43 say '';
44
45 if (not $source or -d $source) {
46         PLP_START {
47                 print "<h1>Source files</h1>";
48         };
49
50         if ($source and $source ne 'tools') {
51                 Abort("Directory index not permitted", '403 source not allowed');
52         }
53
54         print "<p>Project code distributed under the AGPL. Please contribute back.</p>";
55         say '<ul>';
56         for (glob($source ? "$source/*" : '*.plp')) {
57                 say '<li>', showlink($_, "/source/$_");
58         }
59         say "</ul>\n";
60 }
61 else {
62         my $href = showlink($source, $source =~ m{\A (\w+) \.plp \z}x && "/$1");
63         PLP_START {
64                 say "<h1>Source of $href</h1>";
65         };
66
67         my $path = $source;
68         if ($source =~ m{(?:/|^)\.}) {
69                 Abort("File request not permitted", '403 source not allowed');
70         }
71         elsif ($source =~ s{::}{/}g or !-e $source) {
72                 $source .= '.pm';
73                 for (0 .. $#INC) {
74                         -e ($_ = "$INC[$_]/$source") or next;
75                         $path = $_;
76                         last;
77                 }
78         }
79         -r $path or Abort("Requested file not found", '404 source not found');
80         my $size = (stat $path)->[7];
81
82         my $cachefile = "source/$source.html";
83         if (-e $cachefile and (stat $cachefile)->[9] >= (stat $path)->[9]) {
84                 say '<pre>';
85                 print decode_utf8(ReadFile($cachefile));
86                 say '</pre>';
87                 exit;
88         }
89         -e or mkdir for $cachefile =~ s{[^/]+\z}{}r; # dirname
90         open my $cache, '>', $cachefile
91                 or Alert("Could not save cache", "Opening $cachefile failed: $!");;
92
93         if (my $hl = eval {
94                 $size < 32_768 or die 'large files take too long to parse';
95                 require Text::VimColor;
96                 Text::VimColor->VERSION(0.12)
97                         or die 'early versions are buggy under FastCGI';
98                 delete $Text::VimColor::SYNTAX_TYPE{Underlined};
99                 return Text::VimColor->new(
100                         file => $path,
101                         vim_options => [@Text::VimColor::VIM_OPTIONS,
102                                 '+:set enc=utf-8',
103                                 '+:let perl_sub_signatures=1',
104                         ],
105                 )->marked;
106         }) {
107                 my %TYPETAG = (
108                         Statement => 'strong',
109                         Error     => 'em',
110                         Todo      => 'em',
111                         PreProc   => 'strong',
112                 );
113
114                 say '<pre>';
115                 foreach (@{$hl}) {
116                         my ($type, $contents) = @{$_};
117                         $contents = decode_utf8($contents);
118                         my $tag = $type && ($TYPETAG{$type} || 'span');
119                         my $line = Text::VimColor::_xml_escape($contents);
120
121                         # link other page sources, stylesheets, and javascript
122                         $line =~ s{ ^(['"]?) \K ($incname) (?=\1$) }{ showlink($2, "/source/$2") }xe
123                                 if !$type || $type eq 'Constant';
124                         # link relative page locations in html output
125                         $line =~ s{ ^(&quot;)\K (/\w{2,}) (?= (?:/\w+)* \1$) }{ showlink($2, "/source$2.plp") }xe
126                                 if $type && $type eq 'Constant';
127                         # link perl module names (Xx::Xx...)
128                         $line =~ s{ ^\s* \K ([A-Z]\w+(?:::\w+)+) (?![^;\s]) }{ showlink($1, "/source/$1") }xe
129                                 if !$type;
130                         # link generator scripts (by tools/...)
131                         $line =~ s{ ^.*? by\  \K (tools/\S+) }{ showlink($1, "/source/$1") }xe
132                                 if $type && $type eq 'Comment';
133
134                         $line = qq(<$tag class="sy-\l$type">$line</$tag>) if $tag;
135                         print $line;
136                         print {$cache} $line if $cache;
137                 }
138                 say '</pre>';
139         }
140         else {
141                 say '<pre>';
142                 print EscapeHTML(decode_utf8(ReadFile($path)));
143                 say '</pre>';
144         }
145
146         say '';
147 }
148