2 * Copyright (c) 2008 Tobias Sarnowski <sarnowski@new-thoughts.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 // components defined in this file
18 const MANPROT_HANDLER_CONTRACTID = "@mozilla.org/network/protocol;1?name=man";
19 const MANPROT_HANDLER_CID = Components.ID("{c2e6b7ab-8141-45e9-8c84-e32a825bb105}");
21 // components used in this file
22 const NS_IOSERVICE_CID = "{9ac9e770-18bc-11d3-9337-00104ba0fd40}";
23 const NS_PREFSERVICE_CONTRACTID = "@mozilla.org/preferences-service;1";
24 const URI_CONTRACTID = "@mozilla.org/network/simple-uri;1";
25 const NS_WINDOWWATCHER_CONTRACTID = "@mozilla.org/embedcomp/window-watcher;1";
26 const STREAMIOCHANNEL_CONTRACTID = "@mozilla.org/network/stream-io-channel;1";
28 // interfaces used in this file
29 const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler;
30 const nsIURI = Components.interfaces.nsIURI;
31 const nsISupports = Components.interfaces.nsISupports;
32 const nsIIOService = Components.interfaces.nsIIOService;
33 const nsIPrefService = Components.interfaces.nsIPrefService;
34 const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
35 const nsIChannel = Components.interfaces.nsIChannel;
38 function disableHTML(textchar)
40 if (textchar == '<') {
42 } else if (textchar == '>') {
44 } else if (textchar == '&') {
51 function transformMan2HTML(manoutput, man_title, man_category)
55 var first_line = true;
56 var char_after_first_line = false;
59 var on_underline = false;
60 for (var n = 0; n < manoutput.length; n++) {
61 var s = manoutput.substr(n, 1);
62 var c = s.charCodeAt(0);
66 if (n < manoutput.length - 1) {
67 s_next = manoutput.substr(n + 1, 1);
68 c_next = s_next.charCodeAt(0);
73 if (n < manoutput.length - 2) {
74 s_nextnext = manoutput.substr(n + 2, 1);
75 c_nextnext = s_nextnext.charCodeAt(0);
78 var s_nextnextnext = '';
79 var c_nextnextnext = 0;
80 if (n < manoutput.length - 3) {
81 s_nextnextnext = manoutput.substr(n + 3, 1);
82 c_nextnextnext = s_nextnextnext.charCodeAt(0);
88 s_prev = manoutput.substr(n - 1, 1);
89 c_prev = s_prev.charCodeAt(0);
95 // new line, close the old one
96 // clear bold/underline
98 // strip the first line
103 htmloutput += '</span>';
107 htmloutput += '</span>';
108 on_underline = false;
113 } else if (s == '+' && c_next == 8 && s_nextnext == '+' && c_nextnextnext == 8) {
115 htmloutput += '</span>';
116 on_underline = false;
119 htmloutput += '<span class="man_bold">';
122 htmloutput += '* '; // it's a bullet
126 // should be already parsed
129 } else if (s == '_' && c_next == 8) {
132 htmloutput += '</span>';
136 htmloutput += '<span class="man_underline">';
139 htmloutput += disableHTML(s_nextnext);
141 } else if (c_next == 8 && s_nextnext == s) {
144 htmloutput += '</span>';
145 on_underline = false;
148 htmloutput += '<span class="man_bold">';
151 htmloutput += disableHTML(s);
155 htmloutput += '</span>';
159 htmloutput += '</span>';
160 on_underline = false;
162 htmloutput += disableHTML(s);
166 var tmp = htmloutput.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
168 // seperate special titles
169 tmp = tmp.replace(/^<span class="man_bold">(.*)<\/span> <span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1 $2" href="#bottomnav">$1 $2</a></h1>');
170 tmp = tmp.replace(/\n+<span class="man_bold">(.*)<\/span> <span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1 $2" href="#bottomnav">$1 $2</a></h1>');
171 tmp = tmp.replace(/^<span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1" href="#bottomnav">$1</a></h1>');
172 tmp = tmp.replace(/\n+<span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1" href="#bottomnav">$1</a></h1>');
175 tmp = tmp.replace(/([\-.\w]+)\((\w+)\)/g, '<a href="man://$2/$1">$1($2)</a>');
177 // get os type and customize design
178 var shell = Components.classes['%%XPCOM_SHELL%%'].getService().wrappedJSObject;
179 var os = shell.exect("uname");
196 htmloutput = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n';
197 htmloutput += ' "http://www.w3.org/TR/html4/loose.dtd">\n\n';
198 htmloutput += '<html>\n';
199 htmloutput += ' <head>\n';
200 htmloutput += ' <title>' + man_title;
202 htmloutput += '(' + man_category + ')';
204 htmloutput += '</title>\n';
205 htmloutput += ' <link rel="shortcut icon" type="image/png" href="resource://fireman/img/logo16x16.png"/>\n';
206 htmloutput += ' <link rel="stylesheet" type="text/css" href="resource://fireman/css/screen.css" media="screen"/>\n';
207 htmloutput += ' <link rel="stylesheet" type="text/css" href="resource://fireman/css/print.css" media="print"/>\n';
208 htmloutput += ' </head>\n';
209 htmloutput += ' <body>\n';
210 htmloutput += ' <div id="main">\n';
211 htmloutput += ' <div id="header">\n';
212 htmloutput += ' <p>\n';
214 htmloutput += ' <img src="resource://fireman/img/os/' + os + '.png" alt="' + os + '" title="FireMan uses the ' + os + ' manpages."/>\n';
215 htmloutput += ' ' + os + ' Manual Page<br/>\n';
217 htmloutput += ' <img src="resource://fireman/img/os/default.png" alt="FireMan" title="FireMan uses the manpages."/>\n';
218 htmloutput += ' Manual Page<br/>\n';
220 htmloutput += ' <strong>' + man_title;
222 htmloutput += '(' + man_category + ')';
224 htmloutput += '</strong>\n';
225 htmloutput += ' </p>\n';
226 htmloutput += ' </div>\n';
227 htmloutput += ' <div id="manpage">\n';
228 htmloutput += ' <pre>\n';
230 htmloutput += ' </pre>\n';
231 htmloutput += ' </div>\n';
232 htmloutput += ' <div id="footer">\n';
233 htmloutput += ' This manual page is rendered by <a href="#" title="The FireMan Homepage">FireMan</a> <a href="#" title="The FireMan Homepage"><img src="resource://fireman/img/logo24x24.png" alt="FireMan" /></a>\n';
234 htmloutput += ' </div>\n';
235 htmloutput += ' </div>\n';
236 htmloutput += ' </body>\n';
237 htmloutput += '</html>\n\n';
241 /***** ProtocolHandler *****/
243 function ManProtocolHandler(scheme)
245 this.scheme = scheme;
248 // attribute defaults
249 ManProtocolHandler.prototype.defaultPort = -1;
250 ManProtocolHandler.prototype.protocolFlags = nsIProtocolHandler.URI_NORELATIVE;
252 ManProtocolHandler.prototype.allowPort = function(aPort, aScheme)
257 ManProtocolHandler.prototype.newURI = function(aSpec, aCharset, aBaseURI)
259 var uri = Components.classes[URI_CONTRACTID].createInstance(nsIURI);
264 ManProtocolHandler.prototype.newChannel = function(aURI)
270 var urlparts = aURI.spec.substr(aURI.spec.indexOf("://") + "://".length).split("/");
272 // possible formats are:
274 // man://<category>/<title>
275 // man://<category>/<machine>/<title>
277 if (urlparts.length == 1) {
280 if (urlparts.length == 2) {
281 category = urlparts[0];
284 if (urlparts.length == 3) {
285 category = urlparts[0];
286 machine = urlparts[1];
290 // now get the man output and parse it
292 // get out components
293 var shell = Components.classes['%%XPCOM_SHELL%%'].getService().wrappedJSObject;
297 mancmd = mancmd + ' -s ' + category;
300 mancmd = mancmd + ' -S ' + machine;
302 mancmd = mancmd + ' ' + title;
303 var output = shell.exect(mancmd);
305 output = transformMan2HTML(output, title, category);
307 var stream = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
308 stream.setData(output, output.length);
310 // create a channel to stream it into firefox browser
311 var channel = Components.classes["@mozilla.org/network/input-stream-channel;1"].createInstance(Components.interfaces.nsIInputStreamChannel);
312 channel.setURI(aURI);
313 channel.contentStream = stream;
318 /***** ManProtocolHandlerFactory *****/
320 function ManProtocolHandlerFactory(scheme)
322 this.scheme = scheme;
325 ManProtocolHandlerFactory.prototype.createInstance = function(outer, iid)
327 if(outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION;
329 if(!iid.equals(nsIProtocolHandler) && !iid.equals(nsISupports))
330 throw Components.results.NS_ERROR_INVALID_ARG;
332 return new ManProtocolHandler(this.scheme);
335 var factory_man = new ManProtocolHandlerFactory("man");
337 /***** ManModule *****/
339 var ManModule = new Object();
341 ManModule.registerSelf = function(compMgr, fileSpec, location, type)
343 compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
345 // register protocol handlers
346 compMgr.registerFactoryLocation(MANPROT_HANDLER_CID,
347 "MAN protocol handler",
348 MANPROT_HANDLER_CONTRACTID,
349 fileSpec, location, type);
352 ManModule.unregisterSelf = function(compMgr, fileSpec, location)
354 compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
356 // unregister our components
357 compMgr.unregisterFactoryLocation(MANPROT_HANDLER_CID, fileSpec);
360 ManModule.getClassObject = function(compMgr, cid, iid)
362 if(!iid.equals(Components.interfaces.nsIFactory))
363 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
365 if(cid.equals(MANPROT_HANDLER_CID)) return factory_man;
367 throw Components.results.NS_ERROR_NO_INTERFACE;
370 ManModule.canUnload = function(compMgr)
372 return true; // our objects can be unloaded
375 /***** Entrypoint *****/
377 function NSGetModule(compMgr, fileSpec)