Revision | 219 (tree) |
---|---|
Time | 2022-02-18 23:41:15 |
Author | mateuszviste |
frozen ver 20220218 to tags
@@ -0,0 +1,60 @@ | ||
1 | +phpamb - an online PHP AMB viewer | |
2 | +Copyright (C) 2020-2022 Mateusz Viste | |
3 | + | |
4 | +phpamb is a PHP application that reads an AMB book and processes it dynamically | |
5 | +into a nicely formatted html output. | |
6 | + | |
7 | +homepage: http://amb.osdn.io | |
8 | + | |
9 | + | |
10 | +=== HISTORY ================================================================== | |
11 | + | |
12 | +ver 20220218: | |
13 | + - characters with special html meaning are properly escaped | |
14 | + | |
15 | +ver 20220212: | |
16 | + - fname can point to a directory instead of an amb file | |
17 | + - when no unicode.map file is present, utf-8 encoding is assumed | |
18 | + | |
19 | +ver 20201218: | |
20 | + - document's title is unicode-converted as well | |
21 | + | |
22 | +ver 20201215: | |
23 | + - links enclosed in <> brackets are properly processed | |
24 | + | |
25 | +ver 20201212: | |
26 | + - implemented AMB 'notice' and 'boring' attributes | |
27 | + - properly closing any open tag when end of document is reached | |
28 | + | |
29 | +ver 20201210: | |
30 | + - special chars are converted to html entities only when strictly required | |
31 | + - filenames in title bar are displayed without extension | |
32 | + - links are built as relative queries (suggested by Jerome Shidel) | |
33 | + - top title is clickable and directs to index (suggested by Robert Riebisch) | |
34 | + - in-text URLs are detected and linkified (suggested by Robert Riebisch) | |
35 | + - implemented unicode.map encoding to output unicode text when possible | |
36 | + - filenames are processed in a case-insensitive way | |
37 | + | |
38 | +ver 20201207: | |
39 | + - first public release | |
40 | + | |
41 | + | |
42 | +=== MIT LICENSE ============================================================== | |
43 | + | |
44 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
45 | +of this software and associated documentation files (the "Software"), to deal | |
46 | +in the Software without restriction, including without limitation the rights | |
47 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
48 | +copies of the Software, and to permit persons to whom the Software is | |
49 | +furnished to do so, subject to the following conditions: | |
50 | + | |
51 | +The above copyright notice and this permission notice shall be included in all | |
52 | +copies or substantial portions of the Software. | |
53 | + | |
54 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
55 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
56 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
57 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
58 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
59 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
60 | +SOFTWARE. |
@@ -0,0 +1,64 @@ | ||
1 | +@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&display=swap'); | |
2 | + | |
3 | +html { | |
4 | + background-color: #eee; | |
5 | +} | |
6 | + | |
7 | +body { | |
8 | + font-size: 1.2em; | |
9 | + font-family: 'Roboto Mono', monospace; | |
10 | + width: 78ch; | |
11 | + background-color: #fff; | |
12 | + margin: 0 auto; | |
13 | + padding: 0.5em; | |
14 | + padding-top: 0; | |
15 | + white-space: pre; | |
16 | + border: 1px #aaa solid; | |
17 | +} | |
18 | + | |
19 | +h1 { | |
20 | + font-size: 1em; | |
21 | + font-weight: bold; | |
22 | + display: inline; | |
23 | +} | |
24 | + | |
25 | +a { | |
26 | + color: #00f; | |
27 | + text-decoration: none; | |
28 | +} | |
29 | + | |
30 | +a:hover { | |
31 | + color: #fff; | |
32 | + background-color: #44f; | |
33 | +} | |
34 | + | |
35 | +a.liketext, a.liketext:hover { | |
36 | + color: inherit; | |
37 | + background-color: inherit; | |
38 | + font-size: inherit; | |
39 | + font-weight: inherit; | |
40 | + text-decoration: inherit; | |
41 | +} | |
42 | + | |
43 | +div { | |
44 | + margin: 0 -0.5em; | |
45 | + padding: 0 0.5em; | |
46 | + display: flex; | |
47 | + justify-content: space-between; | |
48 | + background-color: #666; | |
49 | +} | |
50 | + | |
51 | +span { | |
52 | + color: #eee; | |
53 | + margin: 0; | |
54 | + padding: 0; | |
55 | +} | |
56 | + | |
57 | +span.notice { | |
58 | + color: #330; | |
59 | + background-color: #f70; | |
60 | +} | |
61 | + | |
62 | +span.boring { | |
63 | + color: #888; | |
64 | +} |
@@ -0,0 +1,204 @@ | ||
1 | +<?php | |
2 | +// php reader of AMB files -- turns an AMB book into a web page | |
3 | +// | |
4 | +// Copyright (C) 2020-2022 Mateusz Viste | |
5 | +// http://amb.osdn.io | |
6 | +// | |
7 | +// MIT license | |
8 | +// | |
9 | +// Permission is hereby granted, free of charge, to any person obtaining a copy | |
10 | +// of this software and associated documentation files (the "Software"), to deal | |
11 | +// in the Software without restriction, including without limitation the rights | |
12 | +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
13 | +// copies of the Software, and to permit persons to whom the Software is | |
14 | +// furnished to do so, subject to the following conditions: | |
15 | +// | |
16 | +// The above copyright notice and this permission notice shall be included in all | |
17 | +// copies or substantial portions of the Software. | |
18 | +// | |
19 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
20 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
21 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
22 | +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
23 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
24 | +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
25 | +// SOFTWARE. | |
26 | + | |
27 | +$VERSION = "20220218"; | |
28 | + | |
29 | +function getamafile($ambfname, $amafname) { | |
30 | + if (! is_file($ambfname)) { | |
31 | + // if its a flat dir, just load the file | |
32 | + if (is_dir($ambfname)) return(file_get_contents($ambfname . '/' . $amafname)); | |
33 | + return(FALSE); | |
34 | + } | |
35 | + $fd = fopen($ambfname, "rb"); | |
36 | + if ($fd === FALSE) return(FALSE); | |
37 | + // read header (AMB1) | |
38 | + if (fread($fd, 4) !== 'AMB1') return(FALSE); | |
39 | + // read number of ama files inside | |
40 | + $fcount = reset(unpack('v', fread($fd, 2))); | |
41 | + if ($fcount === FALSE) return(FALSE); | |
42 | + // read index until AMA file is found | |
43 | + $offset = 0; | |
44 | + $amalen = 0; | |
45 | + $amafile = ''; | |
46 | + for ($i = 0; $i < $fcount; $i++) { | |
47 | + $amafile = rtrim(fread($fd, 12), "\0"); | |
48 | + $offset = reset(unpack('V', fread($fd, 4))); | |
49 | + $amalen = reset(unpack('v', fread($fd, 2))); | |
50 | + $bsum = reset(unpack('v', fread($fd, 2))); | |
51 | + if (strcasecmp($amafile, $amafname) == 0) break; | |
52 | + } | |
53 | + if ($i >= $fcount) return(FALSE); // not found | |
54 | + // jump to offset and read file | |
55 | + fseek($fd, $offset); | |
56 | + $result = fread($fd, $amalen); | |
57 | + fclose($fd); | |
58 | + return($result); | |
59 | +} | |
60 | + | |
61 | + | |
62 | +// converts str into utf-8 using the unicodemap lookup table and returns the resulting (converted) str | |
63 | +function txttoutf8($str, $unicodemap) { | |
64 | + $s = str_split($str, 1); // convert the string to a table for | |
65 | + $res = ''; | |
66 | + foreach ($s as $c) $res .= $unicodemap[ord($c)]; // convert raw characters into HTML unicode codes | |
67 | + return($res); | |
68 | +} | |
69 | + | |
70 | + | |
71 | +// MAIN STARTS HERE | |
72 | + | |
73 | + | |
74 | +if (empty($_GET['fname'])) { | |
75 | + echo "usage: phpamb.php?fname=file.amb\n"; | |
76 | + exit(0); | |
77 | +} | |
78 | + | |
79 | +$ambfname = $_GET['fname']; | |
80 | +$f = 'index.ama'; // default file | |
81 | +if (! empty($_GET['f'])) $f = $_GET['f']; | |
82 | + | |
83 | +$title = trim(getamafile($ambfname, 'title')); | |
84 | +if ($title === FALSE) $title = $ambfname; | |
85 | + | |
86 | +$ama = getamafile($ambfname, $f); | |
87 | +if ($ama === FALSE) $ama = 'ERROR: FILE NOT FOUND'; | |
88 | + | |
89 | +// prepare a 256-entries lookup array for unicode encoding | |
90 | +$unicodemap = array(); | |
91 | +for ($i = 0; $i < 128; $i++) $unicodemap[$i] = $i; // low ascii is the same | |
92 | + | |
93 | +$unicodemaptemp = unpack('v128', getamafile($ambfname, 'unicode.map')); | |
94 | +if ($unicodemaptemp === FALSE) { | |
95 | + $unicodemap = FALSE; | |
96 | +} else { | |
97 | + $unicodemap = array_merge($unicodemap, $unicodemaptemp); | |
98 | + /* convert the unicode map so it contains actual html code instead of glyph values */ | |
99 | + for ($i = 0; $i < 256; $i++) { | |
100 | + if ($unicodemap[$i] < 128) { | |
101 | + $unicodemap[$i] = htmlspecialchars(chr($unicodemap[$i]), ENT_HTML5); | |
102 | + } else { | |
103 | + $unicodemap[$i] = '&#' . $unicodemap[$i] . ';'; | |
104 | + } | |
105 | + } | |
106 | + // perform UTF-8 conversion of the title | |
107 | + $title = txttoutf8($title, $unicodemap); | |
108 | +} | |
109 | + | |
110 | + | |
111 | +echo "<!DOCTYPE html>\n"; | |
112 | +echo "<html>\n"; | |
113 | +echo "<head>\n"; | |
114 | +echo ' <meta charset="UTF-8">' . "\n"; | |
115 | +echo " <title>{$title}</title>\n"; | |
116 | +echo ' <link rel="stylesheet" href="phpamb.css">' . "\n"; | |
117 | +echo ' <meta name="viewport" content="width=device-width, initial-scale=1">' . "\n"; | |
118 | +echo " <meta name=\"generator\" content=\"phpamb/{$VERSION}\">\n"; | |
119 | +echo "</head>\n"; | |
120 | +echo "<body>"; | |
121 | + | |
122 | +echo "<div><span><a href=\"?fname={$ambfname}\" class=\"liketext\">{$title}</a></span><span>[" . pathinfo($f, PATHINFO_FILENAME) . "]</span></div>\n"; | |
123 | + | |
124 | +/* detect links first, before any htmlization occurs */ | |
125 | +$ama = preg_replace('!(https?|ftp)://([-A-Z0-9./_*?&;%=#~:]+)!i', 'LiNkStArTxXx$0LiNkEnDxXx', $ama); | |
126 | + | |
127 | +if ($unicodemap !== FALSE) { | |
128 | + $amacontent = str_split($ama, 1); | |
129 | +} else { | |
130 | + $amacontent = mb_str_split($ama, 1, 'utf-8'); | |
131 | +} | |
132 | +$escnow = 0; // next char is an escape code | |
133 | +$readlink = 0; // a link target is being read | |
134 | +$opentag = ''; // do I have a currently open html tag? | |
135 | + | |
136 | +$out = ''; | |
137 | +foreach ($amacontent as $c) { | |
138 | + // ignore CR | |
139 | + if ($c == "\r") continue; | |
140 | + // is link target being read? | |
141 | + if ($readlink != 0) { | |
142 | + if (($c == "\n") || ($c == ':')) { | |
143 | + $out .= '">'; | |
144 | + $opentag = 'a'; | |
145 | + $readlink = 0; | |
146 | + if ($c == ':') continue; | |
147 | + } else { | |
148 | + $out .= urlencode($c); | |
149 | + } | |
150 | + continue; | |
151 | + } | |
152 | + // | |
153 | + if ($escnow != 0) { | |
154 | + if ($c == '%') { | |
155 | + $out .= '%'; | |
156 | + } else if ($c == 'l') { | |
157 | + $out .= '<a href="' . $_SERVER['PHP_SELF'] . "?fname={$ambfname}&f="; | |
158 | + $readlink = 1; | |
159 | + } else if ($c == 'h') { | |
160 | + $out .= '<h1>'; | |
161 | + $opentag = 'h1'; | |
162 | + } else if ($c == '!') { | |
163 | + $out .= '<span class="notice">'; | |
164 | + $opentag = 'span'; | |
165 | + } else if ($c == 'b') { | |
166 | + $out .= '<span class="boring">'; | |
167 | + $opentag = 'span'; | |
168 | + } | |
169 | + $escnow = 0; | |
170 | + continue; | |
171 | + } | |
172 | + // close </a> if open and got LF or new tag | |
173 | + if ((!empty($opentag)) && (($c == "\n") || ($c == "%"))) { | |
174 | + $out .= "</{$opentag}>"; | |
175 | + $opentag = ''; | |
176 | + } | |
177 | + // | |
178 | + if ($c == '%') { | |
179 | + $escnow = 1; | |
180 | + } else { | |
181 | + if ($unicodemap !== FALSE) { | |
182 | + $out .= $unicodemap[ord($c)]; // convert characters into HTML unicode codes | |
183 | + } else { | |
184 | + $out .= htmlspecialchars($c); | |
185 | + } | |
186 | + } | |
187 | +} | |
188 | + | |
189 | +// close open tags | |
190 | +if (!empty($opentag)) { | |
191 | + $out .= "</{$opentag}>"; | |
192 | + $opentag = ''; | |
193 | +} | |
194 | + | |
195 | +/* postprocessing: find links detected earlier and change them to proper anchors */ | |
196 | +$out = preg_replace('/LiNkStArTxXx.*LiNkEnDxXx/', '<a href="$0">$0</a>', $out); | |
197 | +$out = preg_replace('/(LiNkStArTxXx)|(LiNkEnDxXx)/', '', $out); | |
198 | + | |
199 | +echo $out; | |
200 | + | |
201 | +echo "</body>\n"; | |
202 | +echo "</html>\n"; | |
203 | + | |
204 | +?> |