From de7e5867a65e0a46f1388e3e50bc7eeddd1aecbf Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sun, 27 Jan 2013 01:02:02 -0600 Subject: Rename a number of libraries and executables to avoid conflicts with KDE4 --- tdeioslave/man/CMakeLists.txt | 60 + tdeioslave/man/LICENSE | 16 + tdeioslave/man/Makefile.am | 51 + tdeioslave/man/kio_man.cpp | 1533 +++++++++++ tdeioslave/man/kio_man.css | 21 + tdeioslave/man/kio_man.h | 100 + tdeioslave/man/kio_man_test.cpp | 38 + tdeioslave/man/kmanpart.cpp | 115 + tdeioslave/man/kmanpart.desktop | 91 + tdeioslave/man/kmanpart.h | 79 + tdeioslave/man/man.protocol | 12 + tdeioslave/man/man2html.cpp | 5685 +++++++++++++++++++++++++++++++++++++++ tdeioslave/man/man2html.h | 34 + 13 files changed, 7835 insertions(+) create mode 100644 tdeioslave/man/CMakeLists.txt create mode 100644 tdeioslave/man/LICENSE create mode 100644 tdeioslave/man/Makefile.am create mode 100644 tdeioslave/man/kio_man.cpp create mode 100644 tdeioslave/man/kio_man.css create mode 100644 tdeioslave/man/kio_man.h create mode 100644 tdeioslave/man/kio_man_test.cpp create mode 100644 tdeioslave/man/kmanpart.cpp create mode 100644 tdeioslave/man/kmanpart.desktop create mode 100644 tdeioslave/man/kmanpart.h create mode 100644 tdeioslave/man/man.protocol create mode 100644 tdeioslave/man/man2html.cpp create mode 100644 tdeioslave/man/man2html.h (limited to 'tdeioslave/man') diff --git a/tdeioslave/man/CMakeLists.txt b/tdeioslave/man/CMakeLists.txt new file mode 100644 index 000000000..fca109df0 --- /dev/null +++ b/tdeioslave/man/CMakeLists.txt @@ -0,0 +1,60 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# FIXME not built: man2html, kio_man_test + + +##### system checks ############################# + +check_include_file( "unistd.h" HAVE_UNISTD_H ) +check_include_file( "string.h" HAVE_STRING_H ) + + +##### compiler/linker settings ################## + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES man.protocol kmanpart.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kio_man.css DESTINATION ${DATA_INSTALL_DIR}/tdeio_man ) + + +##### kio_man (module) ########################## + +set( target kio_man ) + +tde_add_kpart( ${target} AUTOMOC + SOURCES man2html.cpp kio_man.cpp + LINK tdeio-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### libkmanpart (module) ###################### + +set( target libkmanpart ) + +tde_add_kpart( ${target} AUTOMOC + SOURCES kmanpart.cpp + LINK tdehtml-shared tdeparts-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/tdeioslave/man/LICENSE b/tdeioslave/man/LICENSE new file mode 100644 index 000000000..d28a48f92 --- /dev/null +++ b/tdeioslave/man/LICENSE @@ -0,0 +1,16 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tdeioslave/man/Makefile.am b/tdeioslave/man/Makefile.am new file mode 100644 index 000000000..d43edb74d --- /dev/null +++ b/tdeioslave/man/Makefile.am @@ -0,0 +1,51 @@ +## Makefile.am of tdebase/tdeioslave/man + +INCLUDES= $(all_includes) +AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_KIO) -ltdetexteditor + +EXTRA_PROGRAMS = kio_man_test man2html + +####### just for testing (j.habenicht@europemail.com, 15.02.2001) + +kio_man_test_SOURCES = kio_man_test.cpp +kio_man_test_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_KIO) -ltdetexteditor +kio_man_test_LDADD = man2html.lo kio_man.lo $(LIB_KIO) $(LIB_TDEUI) $(LIB_TDECORE) $(LIB_QT) + +####### Files + +kde_module_LTLIBRARIES = kio_man.la libkmanpart.la + +kio_man_la_SOURCES = man2html.cpp kio_man.cpp +kio_man_la_LIBADD = $(LIB_KSYCOCA) +kio_man_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +noinst_HEADERS = kio_man.h +### TODO Why is man2htmk.h distributed? + +libkmanpart_la_SOURCES = kmanpart.cpp +libkmanpart_la_LIBADD = -ltdehtml $(LIB_KPARTS) +libkmanpart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) + +kdelnk_DATA = man.protocol kmanpart.desktop +kdelnkdir = $(kde_servicesdir) + +kio_man_data_DATA = kio_man.css +kio_man_datadir = $(kde_datadir)/tdeio_man +EXTRA_DIST=$(kio_man_data_DATA) + +METASOURCES = AUTO + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/tdeio_man.pot + +man2html_SOURCES = dummy.cpp +man2html_LDADD = man2html_simple.o $(LIB_QT) +man2html_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_KIO) -ltdetexteditor + +dummy.cpp: + echo > $@ + +man2html_simple.o: $(srcdir)/man2html.cpp + -rm -f man2html_simple.cpp + $(LN_S) $(srcdir)/man2html.cpp man2html_simple.cpp + $(CXX) $(DEFS) $(DEFAULT_INCLUDES) -DSIMPLE_MAN2HTML $(INCLUDES) $(CPPFLAGS) $(CXXFLAGS) -c man2html_simple.cpp + diff --git a/tdeioslave/man/kio_man.cpp b/tdeioslave/man/kio_man.cpp new file mode 100644 index 000000000..d2ef8cb94 --- /dev/null +++ b/tdeioslave/man/kio_man.cpp @@ -0,0 +1,1533 @@ +/* This file is part of the KDE libraries + Copyright (c) 2000 Matthias Hoelzer-Kluepfel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "kio_man.h" +#include "kio_man.moc" +#include "man2html.h" +#include +#include +#include + +using namespace TDEIO; + +MANProtocol *MANProtocol::_self = 0; + +#define SGML2ROFF_DIRS "/usr/lib/sgml" + +/* + * Drop trailing ".section[.gz]" from name + */ +static +void stripExtension( TQString *name ) +{ + int pos = name->length(); + + if ( name->find(".gz", -3) != -1 ) + pos -= 3; + else if ( name->find(".z", -2, false) != -1 ) + pos -= 2; + else if ( name->find(".bz2", -4) != -1 ) + pos -= 4; + else if ( name->find(".bz", -3) != -1 ) + pos -= 3; + + if ( pos > 0 ) + pos = name->findRev('.', pos-1); + + if ( pos > 0 ) + name->truncate( pos ); +} + +static +bool parseUrl(const TQString& _url, TQString &title, TQString §ion) +{ + section = TQString::null; + + TQString url = _url; + if (url.at(0) == '/') { + if (KStandardDirs::exists(url)) { + title = url; + return true; + } else + { + // If the directory does not exist, then it is perhaps a normal man page + kdDebug(7107) << url << " does not exist" << endl; + } + } + + while (url.at(0) == '/') + url.remove(0,1); + + title = url; + + int pos = url.find('('); + if (pos < 0) + return true; + + title = title.left(pos); + + section = url.mid(pos+1); + section = section.left(section.length()-1); + + return true; +} + + +MANProtocol::MANProtocol(const TQCString &pool_socket, const TQCString &app_socket) + : TQObject(), SlaveBase("man", pool_socket, app_socket) +{ + assert(!_self); + _self = this; + const TQString common_dir = TDEGlobal::dirs()->findResourceDir( "html", "en/common/kde-common.css" ); + const TQString strPath=TQString( "file:%1/en/common" ).arg( common_dir ); + m_htmlPath=strPath.local8Bit(); // ### TODO encode for HTML + m_cssPath=strPath.local8Bit(); // ### TODO encode for CSS + section_names << "1" << "2" << "3" << "3n" << "3p" << "4" << "5" << "6" << "7" + << "8" << "9" << "l" << "n"; + m_manCSSFile = locate( "data", "kio_man/tdeio_man.css" ); +} + +MANProtocol *MANProtocol::self() { return _self; } + +MANProtocol::~MANProtocol() +{ + _self = 0; +} + +void MANProtocol::parseWhatIs( TQMap &i, TQTextStream &t, const TQString &mark ) +{ + TQRegExp re( mark ); + TQString l; + while ( !t.atEnd() ) + { + l = t.readLine(); + int pos = re.search( l ); + if (pos != -1) + { + TQString names = l.left(pos); + TQString descr = l.mid(pos + re.matchedLength()); + while ((pos = names.find(",")) != -1) + { + i[names.left(pos++)] = descr; + while (names[pos] == ' ') + pos++; + names = names.mid(pos); + } + i[names] = descr; + } + } +} + +bool MANProtocol::addWhatIs(TQMap &i, const TQString &name, const TQString &mark) +{ + TQFile f(name); + if (!f.open(IO_ReadOnly)) + return false; + TQTextStream t(&f); + parseWhatIs( i, t, mark ); + return true; +} + +TQMap MANProtocol::buildIndexMap(const TQString §ion) +{ + TQMap i; + TQStringList man_dirs = manDirectories(); + // Supplementary places for whatis databases + man_dirs += m_mandbpath; + if (man_dirs.find("/var/cache/man")==man_dirs.end()) + man_dirs << "/var/cache/man"; + if (man_dirs.find("/var/catman")==man_dirs.end()) + man_dirs << "/var/catman"; + + TQStringList names; + names << "whatis.db" << "whatis"; + TQString mark = "\\s+\\(" + section + "[a-z]*\\)\\s+-\\s+"; + + for ( TQStringList::ConstIterator it_dir = man_dirs.begin(); + it_dir != man_dirs.end(); + ++it_dir ) + { + if ( TQFile::exists( *it_dir ) ) { + TQStringList::ConstIterator it_name; + for ( it_name = names.begin(); + it_name != names.end(); + it_name++ ) + { + if (addWhatIs(i, (*it_dir) + "/" + (*it_name), mark)) + break; + } + if ( it_name == names.end() ) { + TDEProcess proc; + proc << "whatis" << "-M" << (*it_dir) << "-w" << "*"; + myStdStream = TQString::null; + connect( &proc, TQT_SIGNAL( receivedStdout(TDEProcess *, char *, int ) ), + TQT_SLOT( slotGetStdOutput( TDEProcess *, char *, int ) ) ); + proc.start( TDEProcess::Block, TDEProcess::Stdout ); + TQTextStream t( &myStdStream, IO_ReadOnly ); + parseWhatIs( i, t, mark ); + } + } + } + return i; +} + +TQStringList MANProtocol::manDirectories() +{ + checkManPaths(); + // + // Build a list of man directories including translations + // + TQStringList man_dirs; + + for ( TQStringList::ConstIterator it_dir = m_manpath.begin(); + it_dir != m_manpath.end(); + it_dir++ ) + { + // Translated pages in "/" if the directory + // exists + TQStringList languages = TDEGlobal::locale()->languageList(); + + for (TQStringList::ConstIterator it_lang = languages.begin(); + it_lang != languages.end(); + it_lang++ ) + { + if ( !(*it_lang).isEmpty() && (*it_lang) != TQString("C") ) { + TQString dir = (*it_dir) + '/' + (*it_lang); + + struct stat sbuf; + + if ( ::stat( TQFile::encodeName( dir ), &sbuf ) == 0 + && S_ISDIR( sbuf.st_mode ) ) + { + const TQString p = TQDir(dir).canonicalPath(); + if (!man_dirs.contains(p)) man_dirs += p; + } + } + } + + // Untranslated pages in "" + const TQString p = TQDir(*it_dir).canonicalPath(); + if (!man_dirs.contains(p)) man_dirs += p; + } + return man_dirs; +} + +TQStringList MANProtocol::findPages(const TQString &_section, + const TQString &title, + bool full_path) +{ + TQString section = _section; + + TQStringList list; + + // kdDebug() << "findPages '" << section << "' '" << title << "'\n"; + if (title.at(0) == '/') { + list.append(title); + return list; + } + + const TQString star( "*" ); + + // + // Find man sections in this directory + // + TQStringList sect_list; + if ( section.isEmpty() ) + section = star; + + if ( section != star ) + { + // + // Section given as argument + // + sect_list += section; + while (section.at(section.length() - 1).isLetter()) { + section.truncate(section.length() - 1); + sect_list += section; + } + } else { + sect_list += section; + } + + TQStringList man_dirs = manDirectories(); + + // + // Find man pages in the sections listed above + // + for ( TQStringList::ConstIterator it_sect = sect_list.begin(); + it_sect != sect_list.end(); + it_sect++ ) + { + TQString it_real = (*it_sect).lower(); + // + // Find pages + // + for ( TQStringList::ConstIterator it_dir = man_dirs.begin(); + it_dir != man_dirs.end(); + it_dir++ ) + { + TQString man_dir = (*it_dir); + + // + // Sections = all sub directories "man*" and "sman*" + // + DIR *dp = ::opendir( TQFile::encodeName( man_dir ) ); + + if ( !dp ) + continue; + + struct dirent *ep; + + const TQString man = TQString("man"); + const TQString sman = TQString("sman"); + + while ( (ep = ::readdir( dp )) != 0L ) { + const TQString file = TQFile::decodeName( ep->d_name ); + TQString sect = TQString::null; + + if ( file.startsWith( man ) ) + sect = file.mid(3); + else if (file.startsWith(sman)) + sect = file.mid(4); + + if (sect.lower()==it_real) it_real = sect; + + // Only add sect if not already contained, avoid duplicates + if (!sect_list.contains(sect) && _section.isEmpty()) { + kdDebug() << "another section " << sect << endl; + sect_list += sect; + } + } + + ::closedir( dp ); + + if ( *it_sect != star ) { // in that case we only look around for sections + const TQString dir = man_dir + TQString("/man") + (it_real) + '/'; + const TQString sdir = man_dir + TQString("/sman") + (it_real) + '/'; + + findManPagesInSection(dir, title, full_path, list); + findManPagesInSection(sdir, title, full_path, list); + } + } + } + +// kdDebug(7107) << "finished " << list << " " << sect_list << endl; + + return list; +} + +void MANProtocol::findManPagesInSection(const TQString &dir, const TQString &title, bool full_path, TQStringList &list) +{ + kdDebug() << "findManPagesInSection " << dir << " " << title << endl; + bool title_given = !title.isEmpty(); + + DIR *dp = ::opendir( TQFile::encodeName( dir ) ); + + if ( !dp ) + return; + + struct dirent *ep; + + while ( (ep = ::readdir( dp )) != 0L ) { + if ( ep->d_name[0] != '.' ) { + + TQString name = TQFile::decodeName( ep->d_name ); + + // check title if we're looking for a specific page + if ( title_given ) { + if ( !name.startsWith( title ) ) { + continue; + } + else { + // beginning matches, do a more thorough check... + TQString tmp_name = name; + stripExtension( &tmp_name ); + if ( tmp_name != title ) + continue; + } + } + + if ( full_path ) + name.prepend( dir ); + + list += name ; + } + } + ::closedir( dp ); +} + +void MANProtocol::output(const char *insert) +{ + if (insert) + { + m_outputBuffer.writeBlock(insert,strlen(insert)); + } + if (!insert || m_outputBuffer.at() >= 2048) + { + m_outputBuffer.close(); + data(m_outputBuffer.buffer()); + m_outputBuffer.setBuffer(TQByteArray()); + m_outputBuffer.open(IO_WriteOnly); + } +} + +// called by man2html +char *read_man_page(const char *filename) +{ + return MANProtocol::self()->readManPage(filename); +} + +// called by man2html +void output_real(const char *insert) +{ + MANProtocol::self()->output(insert); +} + +static TQString text2html(const TQString& txt) +{ + TQString reply = txt; + + reply = reply.replace('&', "&"); + reply = reply.replace('<', "<"); + reply = reply.replace('>', ">"); + reply = reply.replace('"', "&dquot;"); + reply = reply.replace('\'', """); + return reply; +} + +void MANProtocol::get(const KURL& url ) +{ + kdDebug(7107) << "GET " << url.url() << endl; + + TQString title, section; + + if (!parseUrl(url.path(), title, section)) + { + showMainIndex(); + return; + } + + // see if an index was requested + if (url.query().isEmpty() && (title.isEmpty() || title == "/" || title == ".")) + { + if (section == "index" || section.isEmpty()) + showMainIndex(); + else + showIndex(section); + return; + } + + // tell the mimetype + mimeType("text/html"); + + const TQStringList foundPages=findPages(section, title); + bool pageFound=true; + if (foundPages.isEmpty()) + { + outputError(i18n("No man page matching to %1 found.

" + "Check that you have not mistyped the name of the page that you want.\n" + "Be careful that you must take care about upper case and lower case characters!
" + "If everything looks correct, then perhaps you need to set a better search path " + "for man pages, be it by the environment variable MANPATH or a matching file " + "in the directory /etc .").arg(text2html(title))); + pageFound=false; + } + else if (foundPages.count()>1) + { + pageFound=false; + //check for the case that there is foo.1 and foo.1.gz found: + // ### TODO make it more generic (other extensions) + if ((foundPages.count()==2) && + (((foundPages[0]+".gz") == foundPages[1]) || + (foundPages[0] == (foundPages[1]+".gz")))) + pageFound=true; + else + outputMatchingPages(foundPages); + } + //yes, we found exactly one man page + + if (pageFound) + { + setResourcePath(m_htmlPath,m_cssPath); + m_outputBuffer.open(IO_WriteOnly); + const TQCString filename=TQFile::encodeName(foundPages[0]); + char *buf = readManPage(filename); + + if (!buf) + { + outputError(i18n("Open of %1 failed.").arg(title)); + finished(); + return; + } + // will call output_real + scan_man_page(buf); + delete [] buf; + + output(0); // flush + + m_outputBuffer.close(); + data(m_outputBuffer.buffer()); + m_outputBuffer.setBuffer(TQByteArray()); + // tell we are done + data(TQByteArray()); + } + finished(); +} + +void MANProtocol::slotGetStdOutput(TDEProcess* /* p */, char *s, int len) +{ + myStdStream += TQString::fromLocal8Bit(s, len); +} + +void MANProtocol::slotGetStdOutputUtf8(TDEProcess* /* p */, char *s, int len) +{ + myStdStream += TQString::fromUtf8(s, len); +} + +char *MANProtocol::readManPage(const char *_filename) +{ + TQCString filename = _filename; + + char *buf = NULL; + + /* Determine type of man page file by checking its path. Determination by + * MIME type with KMimeType doesn't work reliablely. E.g., Solaris 7: + * /usr/man/sman7fs/pcfs.7fs -> text/x-csrc : WRONG + * If the path name constains the string sman, assume that it's SGML and + * convert it to roff format (used on Solaris). */ + //TQString file_mimetype = KMimeType::findByPath(TQString(filename), 0, false)->name(); + if (filename.contains("sman", false)) //file_mimetype == "text/html" || ) + { + myStdStream =TQString::null; + TDEProcess proc; + + /* Determine path to sgml2roff, if not already done. */ + getProgramPath(); + proc << mySgml2RoffPath << filename; + + TQApplication::connect(&proc, TQT_SIGNAL(receivedStdout (TDEProcess *, char *, int)), + this, TQT_SLOT(slotGetStdOutput(TDEProcess *, char *, int))); + proc.start(TDEProcess::Block, TDEProcess::All); + + const TQCString cstr=myStdStream.latin1(); + const int len = cstr.size()-1; + buf = new char[len + 4]; + tqmemmove(buf + 1, cstr.data(), len); + buf[0]=buf[len]='\n'; // Start and end with a end of line + buf[len+1]=buf[len+2]='\0'; // Two additional NUL characters at end + } + else + { + if (TQDir::isRelativePath(filename)) { + kdDebug(7107) << "relative " << filename << endl; + filename = TQDir::cleanDirPath(lastdir + "/" + filename).utf8(); + if (!KStandardDirs::exists(filename)) { // exists perhaps with suffix + lastdir = filename.left(filename.findRev('/')); + TQDir mandir(lastdir); + mandir.setNameFilter(filename.mid(filename.findRev('/') + 1) + ".*"); + filename = lastdir + "/" + TQFile::encodeName(mandir.entryList().first()); + } + kdDebug(7107) << "resolved to " << filename << endl; + } + lastdir = filename.left(filename.findRev('/')); + + myStdStream = TQString::null; + TDEProcess proc; + /* TODO: detect availability of 'man --recode' so that this can go + * upstream */ + proc << "man" << "--recode" << "UTF-8" << filename; + + TQApplication::connect(&proc, TQT_SIGNAL(receivedStdout (TDEProcess *, char *, int)), + this, TQT_SLOT(slotGetStdOutputUtf8(TDEProcess *, char *, int))); + proc.start(TDEProcess::Block, TDEProcess::All); + + const TQCString cstr=myStdStream.utf8(); + const int len = cstr.size()-1; + buf = new char[len + 4]; + tqmemmove(buf + 1, cstr.data(), len); + buf[0]=buf[len]='\n'; // Start and end with a end of line + buf[len+1]=buf[len+2]='\0'; // Two NUL characters at end + } + return buf; +} + + +void MANProtocol::outputError(const TQString& errmsg) +{ + TQByteArray array; + TQTextStream os(array, IO_WriteOnly); + os.setEncoding(TQTextStream::UnicodeUTF8); + + os << "" << endl; + os << "" << endl; + os << "" << i18n("Man output") << "\n" << endl; + if ( !m_manCSSFile.isEmpty() ) + os << "" << endl; + os << "" << endl; + os << i18n("

TDE Man Viewer Error

") << errmsg << "" << endl; + os << "" << endl; + + data(array); +} + +void MANProtocol::outputMatchingPages(const TQStringList &matchingPages) +{ + TQByteArray array; + TQTextStream os(array, IO_WriteOnly); + os.setEncoding(TQTextStream::UnicodeUTF8); + + os << "" << endl; + os << "\n"<" << i18n("Man output") <<"" << endl; + if ( !m_manCSSFile.isEmpty() ) + os << "" << endl; + os << "" <

" << i18n("There is more than one matching man page."); + os << "

\n
    \n"; + + int acckey=1; + for (TQStringList::ConstIterator it = matchingPages.begin(); it != matchingPages.end(); ++it) + { + os<<"
  • "<< *it <<"
    \n
    \n"; + acckey++; + } + os << "
\n"; + os << "
\n"; + os << "

" << i18n("Note: if you read a man page in your language," + " be aware it can contain some mistakes or be obsolete." + " In case of doubt, you should have a look at the English version.") << "

"; + + os << "\n"<" << endl; + os << "" << endl; + os << "" << i18n("UNIX Manual Index") << "" << endl; + if (!m_manCSSFile.isEmpty()) + os << "" << endl; + os << "" << endl; + os << "

" << i18n("UNIX Manual Index") << "

" << endl; + + // ### TODO: why still the environment variable + const TQString sectList = getenv("MANSECT"); + TQStringList sections; + if (sectList.isEmpty()) + sections = buildSectionList(manDirectories()); + else + sections = TQStringList::split(':', sectList); + + os << "" << endl; + + TQStringList::ConstIterator it; + for (it = sections.begin(); it != sections.end(); ++it) + os << "" << endl; + + os << "
" << i18n("Section ") + << *it << "  " << sectionName(*it) << "
" << endl; + + // print footer + os << "" << endl; + + data(array); + finished(); +} + +void MANProtocol::constructPath(TQStringList& constr_path, TQStringList constr_catmanpath) +{ + TQMap manpath_map; + TQMap mandb_map; + + // Add paths from /etc/man.conf + // + // Explicit manpaths may be given by lines starting with "MANPATH" or + // "MANDATORY_MANPATH" (depending on system ?). + // Mappings from $PATH to manpath are given by lines starting with + // "MANPATH_MAP" + + TQRegExp manpath_regex( "^MANPATH\\s" ); + TQRegExp mandatory_regex( "^MANDATORY_MANPATH\\s" ); + TQRegExp manpath_map_regex( "^MANPATH_MAP\\s" ); + TQRegExp mandb_map_regex( "^MANDB_MAP\\s" ); + //TQRegExp section_regex( "^SECTION\\s" ); + TQRegExp space_regex( "\\s+" ); // for parsing manpath map + + TQFile mc("/etc/man.conf"); // Caldera + if (!mc.exists()) + mc.setName("/etc/manpath.config"); // SuSE, Debian + if (!mc.exists()) + mc.setName("/etc/man.config"); // Mandrake + + if (mc.open(IO_ReadOnly)) + { + TQTextStream is(&mc); + is.setEncoding(TQTextStream::Locale); + + while (!is.eof()) + { + const TQString line = is.readLine(); + if ( manpath_regex.search(line, 0) == 0 ) + { + const TQString path = line.mid(8).stripWhiteSpace(); + constr_path += path; + } + else if ( mandatory_regex.search(line, 0) == 0 ) + { + const TQString path = line.mid(18).stripWhiteSpace(); + constr_path += path; + } + else if ( manpath_map_regex.search(line, 0) == 0 ) + { + // The entry is "MANPATH_MAP " + const TQStringList mapping = + TQStringList::split(space_regex, line); + + if ( mapping.count() == 3 ) + { + const TQString dir = TQDir::cleanDirPath( mapping[1] ); + const TQString mandir = TQDir::cleanDirPath( mapping[2] ); + + manpath_map[ dir ] = mandir; + } + } + else if ( mandb_map_regex.search(line, 0) == 0 ) + { + // The entry is "MANDB_MAP " + const TQStringList mapping = + TQStringList::split(space_regex, line); + + if ( mapping.count() == 3 ) + { + const TQString mandir = TQDir::cleanDirPath( mapping[1] ); + const TQString catmandir = TQDir::cleanDirPath( mapping[2] ); + + mandb_map[ mandir ] = catmandir; + } + } + /* sections are not used + else if ( section_regex.find(line, 0) == 0 ) + { + if ( !conf_section.isEmpty() ) + conf_section += ':'; + conf_section += line.mid(8).stripWhiteSpace(); + } + */ + } + mc.close(); + } + + // Default paths + static const char *manpaths[] = { + "/usr/X11/man", + "/usr/X11R6/man", + "/usr/man", + "/usr/local/man", + "/usr/exp/man", + "/usr/openwin/man", + "/usr/dt/man", + "/opt/freetool/man", + "/opt/local/man", + "/usr/tex/man", + "/usr/www/man", + "/usr/lang/man", + "/usr/gnu/man", + "/usr/share/man", + "/usr/motif/man", + "/usr/titools/man", + "/usr/sunpc/man", + "/usr/ncd/man", + "/usr/newsprint/man", + NULL }; + + + int i = 0; + while (manpaths[i]) { + if ( constr_path.findIndex( TQString( manpaths[i] ) ) == -1 ) + constr_path += TQString( manpaths[i] ); + i++; + } + + // Directories in $PATH + // - if a manpath mapping exists, use that mapping + // - if a directory "/man" or "/../man" exists, add it + // to the man path (the actual existence check is done further down) + + if ( ::getenv("PATH") ) { + const TQStringList path = + TQStringList::split( ":", + TQString::fromLocal8Bit( ::getenv("PATH") ) ); + + for ( TQStringList::const_iterator it = path.begin(); + it != path.end(); + ++it ) + { + const TQString dir = TQDir::cleanDirPath( *it ); + TQString mandir = manpath_map[ dir ]; + + if ( !mandir.isEmpty() ) { + // a path mapping exists + if ( constr_path.findIndex( mandir ) == -1 ) + constr_path += mandir; + } + else { + // no manpath mapping, use "/man" and "/../man" + + mandir = dir + TQString( "/man" ); + if ( constr_path.findIndex( mandir ) == -1 ) + constr_path += mandir; + + int pos = dir.findRev( '/' ); + if ( pos > 0 ) { + mandir = dir.left( pos ) + TQString("/man"); + if ( constr_path.findIndex( mandir ) == -1 ) + constr_path += mandir; + } + } + TQString catmandir = mandb_map[ mandir ]; + if ( !mandir.isEmpty() ) + { + if ( constr_catmanpath.findIndex( catmandir ) == -1 ) + constr_catmanpath += catmandir; + } + else + { + // What is the default mapping? + catmandir = mandir; + catmandir.replace("/usr/share/","/var/cache/"); + if ( constr_catmanpath.findIndex( catmandir ) == -1 ) + constr_catmanpath += catmandir; + } + } + } +} + +void MANProtocol::checkManPaths() +{ + static bool inited = false; + + if (inited) + return; + + inited = true; + + const TQString manpath_env = TQString::fromLocal8Bit( ::getenv("MANPATH") ); + //TQString mansect_env = TQString::fromLocal8Bit( ::getenv("MANSECT") ); + + // Decide if $MANPATH is enough on its own or if it should be merged + // with the constructed path. + // A $MANPATH starting or ending with ":", or containing "::", + // should be merged with the constructed path. + + bool construct_path = false; + + if ( manpath_env.isEmpty() + || manpath_env[0] == ':' + || manpath_env[manpath_env.length()-1] == ':' + || manpath_env.contains( "::" ) ) + { + construct_path = true; // need to read config file + } + + // Constucted man path -- consists of paths from + // /etc/man.conf + // default dirs + // $PATH + TQStringList constr_path; + TQStringList constr_catmanpath; // catmanpath + + TQString conf_section; + + if ( construct_path ) + { + constructPath(constr_path, constr_catmanpath); + } + + m_mandbpath=constr_catmanpath; + + // Merge $MANPATH with the constructed path to form the + // actual manpath. + // + // The merging syntax with ":" and "::" in $MANPATH will be + // satisfied if any empty string in path_list_env (there + // should be 1 or 0) is replaced by the constructed path. + + const TQStringList path_list_env = TQStringList::split( ':', manpath_env , true ); + + for ( TQStringList::const_iterator it = path_list_env.begin(); + it != path_list_env.end(); + ++it ) + { + struct stat sbuf; + + TQString dir = (*it); + + if ( !dir.isEmpty() ) { + // Add dir to the man path if it exists + if ( m_manpath.findIndex( dir ) == -1 ) { + if ( ::stat( TQFile::encodeName( dir ), &sbuf ) == 0 + && S_ISDIR( sbuf.st_mode ) ) + { + m_manpath += dir; + } + } + } + else { + // Insert constructed path ($MANPATH was empty, or + // there was a ":" at an end or "::") + + for ( TQStringList::Iterator it2 = constr_path.begin(); + it2 != constr_path.end(); + it2++ ) + { + dir = (*it2); + + if ( !dir.isEmpty() ) { + if ( m_manpath.findIndex( dir ) == -1 ) { + if ( ::stat( TQFile::encodeName( dir ), &sbuf ) == 0 + && S_ISDIR( sbuf.st_mode ) ) + { + m_manpath += dir; + } + } + } + } + } + } + +/* sections are not used + // Sections + TQStringList m_mansect = TQStringList::split( ':', mansect_env, true ); + + const char* default_sect[] = + { "1", "2", "3", "4", "5", "6", "7", "8", "9", "n", 0L }; + + for ( int i = 0; default_sect[i] != 0L; i++ ) + if ( m_mansect.findIndex( TQString( default_sect[i] ) ) == -1 ) + m_mansect += TQString( default_sect[i] ); +*/ + +} + + +//#define _USE_OLD_CODE + +#ifdef _USE_OLD_CODE +#warning "using old code" +#else + +// Define this, if you want to compile with qsort from stdlib.h +// else the Qt Heapsort will be used. +// Note, qsort seems to be a bit faster (~10%) on a large man section +// eg. man section 3 +#define _USE_QSORT + +// Setup my own structure, with char pointers. +// from now on only pointers are copied, no strings +// +// containing the whole path string, +// the beginning of the man page name +// and the length of the name +struct man_index_t { + char *manpath; // the full path including man file + const char *manpage_begin; // pointer to the begin of the man file name in the path + int manpage_len; // len of the man file name +}; +typedef man_index_t *man_index_ptr; + +#ifdef _USE_QSORT +int compare_man_index(const void *s1, const void *s2) +{ + struct man_index_t *m1 = *(struct man_index_t **)s1; + struct man_index_t *m2 = *(struct man_index_t **)s2; + int i; + // Compare the names of the pages + // with the shorter length. + // Man page names are not '\0' terminated, so + // this is a bit tricky + if ( m1->manpage_len > m2->manpage_len) + { + i = tqstrnicmp( m1->manpage_begin, + m2->manpage_begin, + m2->manpage_len); + if (!i) + return 1; + return i; + } + + if ( m1->manpage_len < m2->manpage_len) + { + i = tqstrnicmp( m1->manpage_begin, + m2->manpage_begin, + m1->manpage_len); + if (!i) + return -1; + return i; + } + + return tqstrnicmp( m1->manpage_begin, + m2->manpage_begin, + m1->manpage_len); +} + +#else /* !_USE_QSORT */ +#warning using heapsort +// Set up my own man page list, +// with a special compare function to sort itself +typedef TQPtrList QManIndexListBase; +typedef TQPtrListIterator QManIndexListIterator; + +class QManIndexList : public QManIndexListBase +{ +public: +private: + int compareItems( TQPtrCollection::Item s1, TQPtrCollection::Item s2 ) + { + struct man_index_t *m1 = (struct man_index_t *)s1; + struct man_index_t *m2 = (struct man_index_t *)s2; + int i; + // compare the names of the pages + // with the shorter length + if (m1->manpage_len > m2->manpage_len) + { + i = tqstrnicmp(m1->manpage_begin, + m2->manpage_begin, + m2->manpage_len); + if (!i) + return 1; + return i; + } + + if (m1->manpage_len > m2->manpage_len) + { + + i = tqstrnicmp(m1->manpage_begin, + m2->manpage_begin, + m1->manpage_len); + if (!i) + return -1; + return i; + } + + return tqstrnicmp(m1->manpage_begin, + m2->manpage_begin, + m1->manpage_len); + } +}; + +#endif /* !_USE_QSORT */ +#endif /* !_USE_OLD_CODE */ + + + + +void MANProtocol::showIndex(const TQString& section) +{ + TQByteArray array; + TQTextStream os(array, IO_WriteOnly); + os.setEncoding(TQTextStream::UnicodeUTF8); + + // print header + os << "" << endl; + os << "" << endl; + os << "" << i18n("UNIX Manual Index") << "" << endl; + if ( !m_manCSSFile.isEmpty() ) + os << "" << endl; + os << "" << endl; + os << "
" << endl; + os << "

" << i18n( "Index for Section %1: %2").arg(section).arg(sectionName(section)) << "

" << endl; + + // compose list of search paths ------------------------------------------------------------- + + checkManPaths(); + infoMessage(i18n("Generating Index")); + + // search for the man pages + TQStringList pages = findPages( section, TQString::null ); + + TQMap indexmap = buildIndexMap(section); + + // print out the list + os << "" << endl; + +#ifdef _USE_OLD_CODE + pages.sort(); + + TQMap pagemap; + + TQStringList::ConstIterator page; + for (page = pages.begin(); page != pages.end(); ++page) + { + TQString fileName = *page; + + stripExtension( &fileName ); + + pos = fileName.findRev('/'); + if (pos > 0) + fileName = fileName.mid(pos+1); + + if (!fileName.isEmpty()) + pagemap[fileName] = *page; + + } + + for (TQMap::ConstIterator it = pagemap.begin(); + it != pagemap.end(); ++it) + { + os << "" << endl; + } + +#else /* ! _USE_OLD_CODE */ + +#ifdef _USE_QSORT + + int listlen = pages.count(); + man_index_ptr *indexlist = new man_index_ptr[listlen]; + listlen = 0; + +#else /* !_USE_QSORT */ + + QManIndexList manpages; + manpages.setAutoDelete(TRUE); + +#endif /* _USE_QSORT */ + + TQStringList::const_iterator page; + for (page = pages.begin(); page != pages.end(); ++page) + { + // I look for the beginning of the man page name + // i.e. "bla/pagename.3.gz" by looking for the last "/" + // Then look for the end of the name by searching backwards + // for the last ".", not counting zip extensions. + // If the len of the name is >0, + // store it in the list structure, to be sorted later + + char *manpage_end; + struct man_index_t *manindex = new man_index_t; + manindex->manpath = strdup((*page).utf8()); + + manindex->manpage_begin = strrchr(manindex->manpath, '/'); + if (manindex->manpage_begin) + { + manindex->manpage_begin++; + assert(manindex->manpage_begin >= manindex->manpath); + } + else + { + manindex->manpage_begin = manindex->manpath; + assert(manindex->manpage_begin >= manindex->manpath); + } + + // Skip extension ".section[.gz]" + + char *begin = (char*)(manindex->manpage_begin); + int len = strlen( begin ); + char *end = begin+(len-1); + + if ( len >= 3 && strcmp( end-2, ".gz" ) == 0 ) + end -= 3; + else if ( len >= 2 && strcmp( end-1, ".Z" ) == 0 ) + end -= 2; + else if ( len >= 2 && strcmp( end-1, ".z" ) == 0 ) + end -= 2; + else if ( len >= 4 && strcmp( end-3, ".bz2" ) == 0 ) + end -= 4; + + while ( end >= begin && *end != '.' ) + end--; + + if ( end < begin ) + manpage_end = 0; + else + manpage_end = end; + + if (NULL == manpage_end) + { + // no '.' ending ??? + // set the pointer past the end of the filename + manindex->manpage_len = (*page).length(); + manindex->manpage_len -= (manindex->manpage_begin - manindex->manpath); + assert(manindex->manpage_len >= 0); + } + else + { + manindex->manpage_len = (manpage_end - manindex->manpage_begin); + assert(manindex->manpage_len >= 0); + } + + if (0 < manindex->manpage_len) + { + +#ifdef _USE_QSORT + + indexlist[listlen] = manindex; + listlen++; + +#else /* !_USE_QSORT */ + + manpages.append(manindex); + +#endif /* _USE_QSORT */ + + } + } + + // + // Now do the sorting on the page names + // and the printout afterwards + // While printing avoid duplicate man page names + // + + struct man_index_t dummy_index = {0l,0l,0}; + struct man_index_t *last_index = &dummy_index; + +#ifdef _USE_QSORT + + // sort and print + qsort(indexlist, listlen, sizeof(struct man_index_t *), compare_man_index); + + TQChar firstchar, tmp; + TQString indexLine="
\n"; + if (indexlist[0]->manpage_len>0) + { + firstchar=TQChar((indexlist[0]->manpage_begin)[0]).lower(); + + const TQString appendixstr = TQString( + " [%3]\n" + ).arg(firstchar).arg(firstchar).arg(firstchar); + indexLine.append(appendixstr); + } + os << "
" << endl; + + for (int i=0; imanpage_begin" has not, + // so do compare at most "manindex->manpage_len" of the strings. + if (last_index->manpage_len == manindex->manpage_len && + !tqstrncmp(last_index->manpage_begin, + manindex->manpage_begin, + manindex->manpage_len) + ) + { + continue; + } + + tmp=TQChar((manindex->manpage_begin)[0]).lower(); + if (firstchar != tmp) + { + firstchar = tmp; + os << "" << endl; + + const TQString appendixstr = TQString( + " [%3]\n" + ).arg(firstchar).arg(firstchar).arg(firstchar); + indexLine.append(appendixstr); + } + os << "" << endl; + last_index = manindex; + } + indexLine.append(""); + + for (int i=0; imanpath); // allocated by strdup + delete indexlist[i]; + } + + delete [] indexlist; + +#else /* !_USE_QSORT */ + + manpages.sort(); // using + + for (QManIndexListIterator mit(manpages); + mit.current(); + ++mit ) + { + struct man_index_t *manindex = mit.current(); + + // tqstrncmp(): + // "last_man" has already a \0 string ending, but + // "manindex->manpage_begin" has not, + // so do compare at most "manindex->manpage_len" of the strings. + if (last_index->manpage_len == manindex->manpage_len && + !tqstrncmp(last_index->manpage_begin, + manindex->manpage_begin, + manindex->manpage_len) + ) + { + continue; + } + + os << "" << endl; + last_index = manindex; + } +#endif /* _USE_QSORT */ +#endif /* _USE_OLD_CODE */ + + os << "
\n" + << it.key() << "  " + << (indexmap.contains(it.key()) ? indexmap[it.key()] : "" ) + << "
\n " << firstchar <<"\n
\n " << firstchar << "\n
manpath << "\">\n"; + + ((char *)manindex->manpage_begin)[manindex->manpage_len] = '\0'; + os << manindex->manpage_begin + << "  " + << (indexmap.contains(manindex->manpage_begin) ? indexmap[manindex->manpage_begin] : "" ) + << "
manpath << "\">\n"; + + manindex->manpage_begin[manindex->manpage_len] = '\0'; + os << manindex->manpage_begin + << "  " + << (indexmap.contains(manindex->manpage_begin) ? indexmap[manindex->manpage_begin] : "" ) + << "
" << endl; + + os << indexLine << endl; + + // print footer + os << "" << endl; + + infoMessage(TQString::null); + mimeType("text/html"); + data(array); + finished(); +} + +void MANProtocol::listDir(const KURL &url) +{ + kdDebug( 7107 ) << "ENTER listDir: " << url.prettyURL() << endl; + + TQString title; + TQString section; + + if ( !parseUrl(url.path(), title, section) ) { + error( TDEIO::ERR_MALFORMED_URL, url.url() ); + return; + } + + TQStringList list = findPages( section, TQString::null, false ); + + UDSEntryList uds_entry_list; + UDSEntry uds_entry; + UDSAtom uds_atom; + + uds_atom.m_uds = TDEIO::UDS_NAME; // we only do names... + uds_entry.append( uds_atom ); + + TQStringList::Iterator it = list.begin(); + TQStringList::Iterator end = list.end(); + + for ( ; it != end; ++it ) { + stripExtension( &(*it) ); + + uds_entry[0].m_str = *it; + uds_entry_list.append( uds_entry ); + } + + listEntries( uds_entry_list ); + finished(); +} + +void MANProtocol::getProgramPath() +{ + if (!mySgml2RoffPath.isEmpty()) + return; + + mySgml2RoffPath = TDEGlobal::dirs()->findExe("sgml2roff"); + if (!mySgml2RoffPath.isEmpty()) + return; + + /* sgml2roff isn't found in PATH. Check some possible locations where it may be found. */ + mySgml2RoffPath = TDEGlobal::dirs()->findExe("sgml2roff", TQString(SGML2ROFF_DIRS)); + if (!mySgml2RoffPath.isEmpty()) + return; + + /* Cannot find sgml2roff programm: */ + outputError(i18n("Could not find the sgml2roff program on your system. Please install it, if necessary, and extend the search path by adjusting the environment variable PATH before starting TDE.")); + finished(); + exit(); +} diff --git a/tdeioslave/man/kio_man.css b/tdeioslave/man/kio_man.css new file mode 100644 index 000000000..8a9f378bc --- /dev/null +++ b/tdeioslave/man/kio_man.css @@ -0,0 +1,21 @@ +body {background-color:#fffff} + +/*for the list of one manpage section*/ +.secidxshort { + display:block; + position:absolute;overflow:auto; + top:0px;bottom:95%;left:0;right:0; +} +.secidxmain { +/*misfortunately accessing anchors in a scrollview + doesn't seem to work yet with konqi, so:*/ +/* + position:absolute;overflow:auto; + top:5%;bottom:0;left:0;right:0; +*/ +} +.secidxnextletter { + font-size:larger; + border-bottom:1px solid black; + text-align:center +} diff --git a/tdeioslave/man/kio_man.h b/tdeioslave/man/kio_man.h new file mode 100644 index 000000000..829a07964 --- /dev/null +++ b/tdeioslave/man/kio_man.h @@ -0,0 +1,100 @@ +/* This file is part of the KDE libraries + Copyright (c) 2000 Matthias Hoelzer-Kluepfel + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __kio_man_h__ +#define __kio_man_h__ + + +#include +#include +#include +#include +#include + + +#include +#include + + +class MANProtocol : public TQObject, public TDEIO::SlaveBase +{ + Q_OBJECT + +public: + + MANProtocol(const TQCString &pool_socket, const TQCString &app_socket); + virtual ~MANProtocol(); + + virtual void get(const KURL& url); + virtual void stat(const KURL& url); + + virtual void mimetype(const KURL &url); + virtual void listDir(const KURL &url); + + void outputError(const TQString& errmsg); + void outputMatchingPages(const TQStringList &matchingPages); + + void showMainIndex(); + void showIndex(const TQString& section); + + // the following two functions are the interface to man2html + void output(const char *insert); + char *readManPage(const char *filename); + + static MANProtocol *self(); + +private slots: + void slotGetStdOutput(TDEProcess*, char*, int); + void slotGetStdOutputUtf8(TDEProcess*, char*, int); + +private: + void checkManPaths(); + TQStringList manDirectories(); + TQMap buildIndexMap(const TQString& section); + bool addWhatIs(TQMap& i, const TQString& f, const TQString& mark); + void parseWhatIs( TQMap &i, TQTextStream &t, const TQString &mark ); + TQStringList findPages(const TQString& section, + const TQString &title, + bool full_path = true); + + void addToBuffer(const char *buffer, int buflen); + TQString pageName(const TQString& page) const; + TQStringList buildSectionList(const TQStringList& dirs) const; + void constructPath(TQStringList& constr_path, TQStringList constr_catmanpath); +private: + static MANProtocol *_self; + TQCString lastdir; + + void findManPagesInSection(const TQString &dir, const TQString &title, bool full_path, TQStringList &list); + TQStringList m_manpath; ///< Path of man directories + TQStringList m_mandbpath; ///< Path of catman directories + TQStringList section_names; + + TQString myStdStream; + TQString mySgml2RoffPath; + void getProgramPath(); + + TQCString m_htmlPath; ///< Path to TDE resources, encoded for HTML + TQCString m_cssPath; ///< Path to TDE resources, encoded for CSS + TQBuffer m_outputBuffer; ///< Buffer for the output + TQString m_manCSSFile; ///< Path to kio_man.css +}; + + +#endif diff --git a/tdeioslave/man/kio_man_test.cpp b/tdeioslave/man/kio_man_test.cpp new file mode 100644 index 000000000..a181c47b3 --- /dev/null +++ b/tdeioslave/man/kio_man_test.cpp @@ -0,0 +1,38 @@ + + +#include + +#include "kio_man.h" + + +#include +#include + + +class kio_man_test : public MANProtocol +{ + Q_OBJECT + +public: + kio_man_test(const TQCString &pool_socket, const TQCString &app_socket); + +protected: + virtual void data(int); + +}; + + + + + +int main(int argc, char **argv) +{ + TDEApplication a( argc, argv , "p2"); + + MANProtocol testproto("/tmp/tdeiotest.in", "/tmp/tdeiotest.out"); + testproto.showIndex("3"); + + return 0; +} + + diff --git a/tdeioslave/man/kmanpart.cpp b/tdeioslave/man/kmanpart.cpp new file mode 100644 index 000000000..01768e33d --- /dev/null +++ b/tdeioslave/man/kmanpart.cpp @@ -0,0 +1,115 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Alexander Neundorf + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kmanpart.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +extern "C" +{ + KDE_EXPORT void* init_libkmanpart() + { + return new KManPartFactory; + } +} + +TDEInstance* KManPartFactory::s_instance = 0L; +TDEAboutData* KManPartFactory::s_about = 0L; + +KManPartFactory::KManPartFactory( TQObject* parent, const char* name ) + : KParts::Factory( parent, name ) +{} + +KManPartFactory::~KManPartFactory() +{ + delete s_instance; + s_instance = 0L; + delete s_about; +} + +KParts::Part* KManPartFactory::createPartObject( TQWidget * parentWidget, const char* /*widgetName*/, TQObject *, + const char* name, const char* /*className*/,const TQStringList & ) +{ + KManPart* part = new KManPart(parentWidget, name ); + return part; +} + +TDEInstance* KManPartFactory::instance() +{ + if( !s_instance ) + { + s_about = new TDEAboutData( "kmanpart", + I18N_NOOP( "KMan" ), TDE_VERSION_STRING ); + s_instance = new TDEInstance( s_about ); + } + return s_instance; +} + + +KManPart::KManPart( TQWidget * parent, const char * name ) +: KHTMLPart( parent, name ) +,m_job(0) +{ + TDEInstance * instance = new TDEInstance( "kmanpart" ); + setInstance( instance ); + m_extension=new KParts::BrowserExtension(this); +} + +bool KManPart::openURL( const KURL &url ) +{ + return KParts::ReadOnlyPart::openURL(url); +} + +bool KManPart::openFile() +{ + if (m_job!=0) + m_job->kill(); + + begin(); + + KURL url; + url.setProtocol( "man" ); + url.setPath( m_file ); + + m_job = TDEIO::get( url, true, false ); + connect( m_job, TQT_SIGNAL( data( TDEIO::Job *, const TQByteArray &) ), TQT_SLOT( readData( TDEIO::Job *, const TQByteArray &) ) ); + connect( m_job, TQT_SIGNAL( result( TDEIO::Job * ) ), TQT_SLOT( jobDone( TDEIO::Job * ) ) ); + return true; +} + +void KManPart::readData(TDEIO::Job * , const TQByteArray & data) +{ + write(data,data.size()); +} + +void KManPart::jobDone( TDEIO::Job *) +{ + m_job=0; + end(); +} + +#include "kmanpart.moc" + diff --git a/tdeioslave/man/kmanpart.desktop b/tdeioslave/man/kmanpart.desktop new file mode 100644 index 000000000..220e19b84 --- /dev/null +++ b/tdeioslave/man/kmanpart.desktop @@ -0,0 +1,91 @@ +[Desktop Entry] +Type=Service +Comment=Embeddable Troff Viewer +Comment[af]=Inlegbare Troff Kyker +Comment[ar]=مستعرض Troff المدمج +Comment[be]=Убудаваны праглядальнік Troff +Comment[bg]=Визуализатор за вграждане на Troff +Comment[bn]=সন্নিবেশযোগ্য ট্রফ্ প্রদর্শক +Comment[bs]=Ugradivi preglednik Troff datoteka +Comment[ca]=Visor Troff encastable +Comment[cs]=Komponenta pro zobrazování manuálových stránek +Comment[csb]=Przezérnik lopków troff +Comment[cy]=Gwelydd Troff Mewnadeiladwy +Comment[da]=Indlejrbar Troff-fremviser +Comment[de]=Eingebetteter Troff-Betrachter +Comment[el]=Ενσωματώσιμος προβολέας Troff +Comment[eo]=Enkonstruebla bildrigardilo +Comment[es]=Visor empotrable de Troff +Comment[et]=Põimitav Troff komponent +Comment[eu]=Troff ikustailu txertagarria +Comment[fa]=مشاهده‌گر Troff نهفتنی +Comment[fi]=Upotettava Troff-näytin +Comment[fr]=Afficheur troff incorporé +Comment[fy]=Ynsletten Troff-werjefteprogramma +Comment[ga]=Amharcán Inleabaithe troff +Comment[gl]=Visor de Troff Incrustábel +Comment[he]=מציג Troff בר־הטבעה +Comment[hi]=एम्बेडेबल ट्राफ प्रदर्शक +Comment[hr]=Ugradivi preglednik slika +Comment[hu]=Beágyazható Troff-komponens +Comment[is]=Ívefjanlegur troff skoðari +Comment[it]=Visualizzatore integrabile di file Troff +Comment[ja]=埋め込み Troff ビューア +Comment[ka]=ჩაშენებული დამთვალიერებელი პროგრამა Troff +Comment[kk]=Құрамына енгізілетін Troff қарау құралы +Comment[km]=កម្មវិធី​មើល Troff ដែល​អាច​បង្កប់​បាន +Comment[ko]=포함 가능한 Troff 뷰어 +Comment[lo]=ຄອມໂປເນັນຕົວສະແດງພາບທີ່ຝັ່ງໄດ້ +Comment[lt]=Įdedamas Troff žiūriklis +Comment[lv]=Iegults Troff skatītājs +Comment[mk]=Вгнездлив Troff прегледувач +Comment[mn]=Холбох боломжит Troff-Харагч +Comment[ms]=Pemapar Troff Boleh Benam +Comment[mt]=Werrej integrat Troff +Comment[nb]=Innebyggbar Troff-viser +Comment[nds]=Kieker för Troff, de inbett warrn kann +Comment[ne]=सम्मिलित गर्न सकिने ट्रफ दर्शक +Comment[nl]=Ingebed Troff-weergaveprogramma +Comment[nn]=Innebyggbar Troff-visar +Comment[nso]=Selebeledi seo se Robaditswego sa Troff +Comment[pa]=ਸ਼ਾਮਿਲ ਹੋਣਯੋਗ Troff ਦਰਸ਼ਕ +Comment[pl]=Przeglądania plików troff +Comment[pt]=Visualizador de Troff incorporado +Comment[pt_BR]=Visualizar Troff integrado +Comment[ro]=Componentă de vizualizare Troff înglobată +Comment[ru]=Встраиваемая программа просмотра Troff +Comment[rw]=Mugaragaza Troff Ishyirwamo +Comment[se]=Vuojohahtti Troff-cájeheaddji +Comment[sk]=Vložiteľný prehliadač Troff +Comment[sl]=Vključen pregledovalnik za Troff +Comment[sr]=Уградиви Troff приказивач +Comment[sr@Latn]=Ugradivi Troff prikazivač +Comment[sv]=Inbäddningsbar Troff-visare +Comment[ta]=உட்பொதிந்த ட்ராஃப் காட்சி +Comment[te]=పొదగదగ్గ ట్రాఫ్ వీక్షిణి +Comment[tg]=Биношавандаи барномаи намоишгари Troff +Comment[th]=โปรแกรมดู Troff ที่สามารถฝังตัวได้ +Comment[tr]=Gömülebilir Troff Görüntüleyici +Comment[tt]=Quşılulı Troff-Kürsätkeç +Comment[uk]=Вмонтований переглядач Troff +Comment[uz]=Ichiga oʻrnatib boʻladigan Troff-faylini koʻruvchi +Comment[uz@cyrillic]=Ичига ўрнатиб бўладиган Troff-файлини кўрувчи +Comment[ven]=Tshivhoni tsha Troff tsho dzheniswaho +Comment[vi]=Trình xem Troff nhúng được +Comment[wa]=Ravalåve håyneu di fitchîs Troff +Comment[xh]=Umboniseli we Troff Elungiselekayo +Comment[zh_CN]=嵌入的 Troff 查看器 +Comment[zh_TW]=可嵌入的 Troff 檢視元件 +Comment[zu]=Umbukisi we-Troff Oshuthekiwe +MimeType=application/x-troff;application/x-troff-man; +Name=KManPart +Name[eo]=KMan-parto +Name[hi]=के-मेन-पार्ट +Name[lo]=ຕົວຮງກພື້ນທີ່ທຳງານ - K +Name[ne]=K म्यान भाग +Name[pt_BR]=Componente KMan +Name[ro]=Componentă KMan +Name[sv]=Kman-del +Name[te]=కెమేన్ భాగం +ServiceTypes=KParts/ReadOnlyPart,Browser/View +X-TDE-Library=libkmanpart diff --git a/tdeioslave/man/kmanpart.h b/tdeioslave/man/kmanpart.h new file mode 100644 index 000000000..20a451c91 --- /dev/null +++ b/tdeioslave/man/kmanpart.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Alexander Neundorf + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef KMANPART_H +#define KMANPART_H + +#include +#include +#include +#include +#include +#include + +#include + +class TDEInstance; +class TDEAboutData; + +/** + * Man Page Viewer + * \todo: Why is it needed? Why is KHTML alone not possible? + */ +class KManPartFactory: public KParts::Factory +{ + Q_OBJECT + public: + KManPartFactory( TQObject * parent = 0, const char * name = 0 ); + virtual ~KManPartFactory(); + + virtual KParts::Part* createPartObject( TQWidget * parentWidget, const char * widgetName , + TQObject* parent, const char* name, const char * classname, + const TQStringList &args); + + static TDEInstance * instance(); + + private: + static TDEInstance * s_instance; + static TDEAboutData * s_about; + +}; + +class KManPart : public KHTMLPart +{ + Q_OBJECT + public: + KManPart( TQWidget * parent, const char * name = 0L ); + KParts::BrowserExtension * extension() {return m_extension;} + + public slots: + virtual bool openURL( const KURL &url ); + protected slots: + void readData(TDEIO::Job * , const TQByteArray & data); + void jobDone( TDEIO::Job *); + protected: + virtual bool openFile(); + TDEInstance *m_instance; + KParts::BrowserExtension * m_extension; + TDEIO::TransferJob *m_job; +}; + +#endif + diff --git a/tdeioslave/man/man.protocol b/tdeioslave/man/man.protocol new file mode 100644 index 000000000..555970fdc --- /dev/null +++ b/tdeioslave/man/man.protocol @@ -0,0 +1,12 @@ +[Protocol] +exec=kio_man +protocol=man +input=none +output=filesystem +reading=true +listing=Name +defaultMimetype=text/html +determineMimetypeFromExtension=false +DocPath=tdeioslave/man.html +Icon=man +Class=:local diff --git a/tdeioslave/man/man2html.cpp b/tdeioslave/man/man2html.cpp new file mode 100644 index 000000000..ec46b2d3d --- /dev/null +++ b/tdeioslave/man/man2html.cpp @@ -0,0 +1,5685 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2005 Nicolas GOUTTE + + ### TODO: who else? +*/ + +// Start of verbatim comment + +/* +** This program was written by Richard Verhoeven (NL:5482ZX35) +** at the Eindhoven University of Technology. Email: rcb5@win.tue.nl +** +** Permission is granted to distribute, modify and use this program as long +** as this comment is not removed or changed. +*/ + +// End of verbatim comment + +// kate: space-indent on; indent-width 4; replace-tabs on; + +/* + * man2html-linux-1.0/1.1 + * This version modified for Redhat/Caldera linux - March 1996. + * Michael Hamilton . + * + * man2html-linux-1.2 + * Added support for BSD mandoc pages - I didn't have any documentation + * on the mandoc macros, so I may have missed some. + * Michael Hamilton . + * + * vh-man2html-1.3 + * Renamed to avoid confusion (V for Verhoeven, H for Hamilton). + * + * vh-man2html-1.4 + * Now uses /etc/man.config + * Added support for compressed pages. + * Added "length-safe" string operations for client input parameters. + * More secure, -M secured, and client input string lengths checked. + * + */ + +/* +** If you want to use this program for your WWW server, adjust the line +** which defines the CGIBASE or compile it with the -DCGIBASE='"..."' option. +** +** You have to adjust the built-in manpath to your local system. Note that +** every directory should start and end with the '/' and that the first +** directory should be "/" to allow a full path as an argument. +** +** The program first check if PATH_INFO contains some information. +** If it does (t.i. man2html/some/thing is used), the program will look +** for a manpage called PATH_INFO in the manpath. +** +** Otherwise the manpath is searched for the specified command line argument, +** where the following options can be used: +** +** name name of manpage (csh, printf, xv, troff) +** section the section (1 2 3 4 5 6 7 8 9 n l 1v ...) +** -M path an extra directory to look for manpages (replaces "/") +** +** If man2html finds multiple manpages that satisfy the options, an index +** is displayed and the user can make a choice. If only one page is +** found, that page will be displayed. +** +** man2html will add links to the converted manpages. The function add_links +** is used for that. At the moment it will add links as follows, where +** indicates what should match to start with: +** ^^^ +** Recognition Item Link +** ---------------------------------------------------------- +** name(*) Manpage ../man?/name.* +** ^ +** name@hostname Email address mailto:name@hostname +** ^ +** method://string URL method://string +** ^^^ +** www.host.name WWW server http://www.host.name +** ^^^^ +** ftp.host.name FTP server ftp://ftp.host.name +** ^^^^ +** Include file file:/usr/include/file.h +** ^^^ +** +** Since man2html does not check if manpages, hosts or email addresses exist, +** some links might not work. For manpages, some extra checks are performed +** to make sure not every () pair creates a link. Also out of date pages +** might point to incorrect places. +** +** The program will not allow users to get system specific files, such as +** /etc/passwd. It will check that "man" is part of the specified file and +** that "/../" isn't. Even if someone manages to get such file, man2html will +** handle it like a manpage and will usually not produce any output (or crash). +** +** If you find any bugs when normal manpages are converted, please report +** them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle +** the manpage correct. +** +** Known bugs and missing features: +** +** * Equations are not converted at all. +** * Tables are converted but some features are not possible in html. +** * The tabbing environment is converted by counting characters and adding +** spaces. This might go wrong (outside
)
+**  * Some manpages rely on the fact that troff/nroff is used to convert
+**    them and use features which are not descripted in the man manpages.
+**    (definitions, calculations, conditionals, requests). I can't guarantee
+**    that all these features work on all manpages. (I didn't have the
+**    time to look through all the available manpages.)
+*/
+
+#ifdef SIMPLE_MAN2HTML
+  // We suppose that we run on a standard Linux
+# define HAVE_STRING_H 1
+# define HAVE_UNISTD_H 1
+#else
+# include 
+#endif
+
+#include 
+
+#ifdef HAVE_UNISTD_H
+# include 
+#endif
+
+#ifdef HAVE_STRING_H
+# include 
+#endif
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifdef SIMPLE_MAN2HTML
+# include 
+# include 
+# include 
+# include 
+# define kdDebug(x) cerr
+# define kdWarning(x) cerr << "WARNING "
+#else
+# include 
+# include 
+# include 
+#endif
+
+
+
+#include "man2html.h"
+
+using namespace std;
+
+#define NULL_TERMINATED(n) ((n) + 1)
+
+#define HUGE_STR_MAX  10000
+#define LARGE_STR_MAX 2000
+#define MED_STR_MAX   500
+#define SMALL_STR_MAX 100
+#define TINY_STR_MAX  10
+
+
+#if 1
+// The output is current too horrible to be called HTML 4.01
+#define DOCTYPE ""
+#else
+#define DOCTYPE "\n"
+#endif
+
+/* mdoc(7) Bl/El lists to HTML list types */
+#define BL_DESC_LIST   1
+#define BL_BULLET_LIST 2
+#define BL_ENUM_LIST   4
+
+/* mdoc(7) Bd/Ed example(?) blocks */
+#define BD_LITERAL  1
+#define BD_INDENT   2
+
+static int s_nroff = 1; // NROFF mode by default
+
+static int mandoc_name_count = 0; /* Don't break on the first Nm */
+
+static char *stralloc(int len)
+{
+  /* allocate enough for len + NULL */
+  char *news = new char [len+1];
+#ifdef SIMPLE_MAN2HTML
+  if (!news)
+  {
+    cerr << "man2html: out of memory" << endl;
+    exit(EXIT_FAILURE);
+  }
+#else
+// modern compilers do not return a NULL pointer for a new
+#endif
+  return news;
+}
+
+static char *strlimitcpy(char *to, char *from, int n, int limit)
+{                               /* Assumes space for limit plus a null */
+  const int len = n > limit ? limit : n;
+  tqstrncpy(to, from, len + 1);
+  to[len] = '\0';
+  return to;
+}
+
+/* below this you should not change anything unless you know a lot
+** about this program or about troff.
+*/
+
+
+/// Structure for character definitions
+struct CSTRDEF {
+    int nr, slen;
+    const char *st;
+};
+
+
+
+const char NEWLINE[2]="\n";
+
+/**
+ * Class for defining strings and macros
+ */
+class StringDefinition
+{
+public:
+    StringDefinition( void ) : m_length(0) {}
+    StringDefinition( int len, const char* cstr ) : m_length( len ), m_output( cstr ) {}
+public:
+    int m_length; ///< Length of output text
+    TQCString m_output; ///< Defined string
+};
+
+/**
+ * Class for defining number registers
+ * \note Not for internal read-only registers
+ */
+class NumberDefinition
+{
+    public:
+        NumberDefinition( void ) : m_value(0), m_increment(0) {}
+        NumberDefinition( int value ) : m_value( value ), m_increment(0) {}
+        NumberDefinition( int value, int incr) : m_value( value ), m_increment( incr ) {}
+    public:
+        int m_value; ///< value of number register
+        int m_increment; ///< Increment of number register
+        // ### TODO: display form (.af)
+};
+
+/**
+ * Map of character definitions
+ */
+static TQMap s_characterDefinitionMap;
+
+/**
+ * Map of string variable and macro definitions
+ * \note String variables and macros are the same thing!
+ */
+static TQMap s_stringDefinitionMap;
+
+/**
+ * Map of number registers
+ * \note Intern number registers (starting with a dot are not handled here)
+ */
+static TQMap s_numberDefinitionMap;
+
+static void fill_old_character_definitions( void );
+
+/**
+ * Initialize character variables
+ */
+static void InitCharacterDefinitions( void )
+{
+    fill_old_character_definitions();
+    // ### HACK: as we are converting to HTML too early, define characters with HTML references
+    s_characterDefinitionMap.insert( "<-", StringDefinition( 1, "←" ) ); // <-
+    s_characterDefinitionMap.insert( "->", StringDefinition( 1, "→" ) ); // ->
+    s_characterDefinitionMap.insert( "<>", StringDefinition( 1, "↔" ) ); // <>
+    s_characterDefinitionMap.insert( "<=", StringDefinition( 1, "≤" ) ); // <=
+    s_characterDefinitionMap.insert( ">=", StringDefinition( 1, "≥" ) ); // >=
+    // End HACK
+}
+
+/**
+ * Initialize string variables
+ */
+static void InitStringDefinitions( void )
+{
+    // mdoc-only, see mdoc.samples(7)
+    s_stringDefinitionMap.insert( "<=", StringDefinition( 1, "≤" ) );
+    s_stringDefinitionMap.insert( ">=", StringDefinition( 1, "≥" ) );
+    s_stringDefinitionMap.insert( "Rq", StringDefinition( 1, "”" ) );
+    s_stringDefinitionMap.insert( "Lq", StringDefinition( 1, "“" ) );
+    s_stringDefinitionMap.insert( "ua", StringDefinition( 1, "&circ" ) ); // Note this is different from \(ua
+    s_stringDefinitionMap.insert( "aa", StringDefinition( 1, "´" ) );
+    s_stringDefinitionMap.insert( "ga", StringDefinition( 1, "`" ) );
+    s_stringDefinitionMap.insert( "q",  StringDefinition( 1, """ ) );
+    s_stringDefinitionMap.insert( "Pi", StringDefinition( 1, "π" ) );
+    s_stringDefinitionMap.insert( "Ne", StringDefinition( 1, "≠" ) );
+    s_stringDefinitionMap.insert( "Le", StringDefinition( 1, "≤" ) );
+    s_stringDefinitionMap.insert( "Ge", StringDefinition( 1, "≥" ) );
+    s_stringDefinitionMap.insert( "Lt", StringDefinition( 1, "<" ) );
+    s_stringDefinitionMap.insert( "Gt", StringDefinition( 1, ">" ) );
+    s_stringDefinitionMap.insert( "Pm", StringDefinition( 1, "±" ) );
+    s_stringDefinitionMap.insert( "If", StringDefinition( 1, "∞" ) );
+    s_stringDefinitionMap.insert( "Na", StringDefinition( 3, "NaN" ) );
+    s_stringDefinitionMap.insert( "Ba", StringDefinition( 1, "|" ) );
+    // end mdoc-only
+    // man(7)
+    s_stringDefinitionMap.insert( "Tm", StringDefinition( 1, "™" ) ); // \*(TM
+    s_stringDefinitionMap.insert( "R", StringDefinition( 1, "®" ) ); // \*R
+    // end man(7)
+    // Missing characters from man(7):
+    // \*S "Change to default font size"
+#ifndef SIMPLE_MAN2HTML
+    // Special KDE KIO man:
+    const TQCString tdeversion(TDE_VERSION_STRING);
+    s_stringDefinitionMap.insert( ".TDE_VERSION_STRING", StringDefinition( tdeversion.length(), tdeversion ) );
+#endif
+}
+
+/**
+ * Initialize number registers
+ * \note Internal read-only registers are not handled here
+ */
+static void InitNumberDefinitions( void )
+{
+    // As the date number registers are more for end-users, better choose local time.
+    // Groff seems to support Gregorian dates only
+    TQDate today( TQDate::currentDate( Qt::LocalTime ) );
+    s_numberDefinitionMap.insert( "year", today.year() ); // Y2K-correct year
+    s_numberDefinitionMap.insert( "yr", today.year() - 1900 ); // Y2K-incorrect year
+    s_numberDefinitionMap.insert( "mo", today.month() );
+    s_numberDefinitionMap.insert( "dy", today.day() );
+    s_numberDefinitionMap.insert( "dw", today.dayOfWeek() );
+}
+
+
+#define V(A,B) ((A)*256+(B))
+
+//used in expand_char, e.g. for "\(bu"
+// see groff_char(7) for list
+static CSTRDEF standardchar[] = {
+    { V('*','*'), 1, "*" },
+    { V('*','A'), 1, "Α" },
+    { V('*','B'), 1, "Β" },
+    { V('*','C'), 1, "Ξ" },
+    { V('*','D'), 1, "Δ" },
+    { V('*','E'), 1, "Ε" },
+    { V('*','F'), 1, "Φ" },
+    { V('*','G'), 1, "Γ" },
+    { V('*','H'), 1, "Θ" },
+    { V('*','I'), 1, "Ι" },
+    { V('*','K'), 1, "Κ" },
+    { V('*','L'), 1, "Λ" },
+    { V('*','M'), 1, "&Mu:" },
+    { V('*','N'), 1, "Ν" },
+    { V('*','O'), 1, "Ο" },
+    { V('*','P'), 1, "Π" },
+    { V('*','Q'), 1, "Ψ" },
+    { V('*','R'), 1, "Ρ" },
+    { V('*','S'), 1, "Σ" },
+    { V('*','T'), 1, "Τ" },
+    { V('*','U'), 1, "Υ" },
+    { V('*','W'), 1, "Ω" },
+    { V('*','X'), 1, "Χ" },
+    { V('*','Y'), 1, "Η" },
+    { V('*','Z'), 1, "Ζ" },
+    { V('*','a'), 1, "α"},
+    { V('*','b'), 1, "β"},
+    { V('*','c'), 1, "ξ"},
+    { V('*','d'), 1, "δ"},
+    { V('*','e'), 1, "ε"},
+    { V('*','f'), 1, "φ"},
+    { V('*','g'), 1, "γ"},
+    { V('*','h'), 1, "θ"},
+    { V('*','i'), 1, "ι"},
+    { V('*','k'), 1, "κ"},
+    { V('*','l'), 1, "λ"},
+    { V('*','m'), 1, "μ" },
+    { V('*','n'), 1, "ν"},
+    { V('*','o'), 1, "ο"},
+    { V('*','p'), 1, "π"},
+    { V('*','q'), 1, "ψ"},
+    { V('*','r'), 1, "ρ"},
+    { V('*','s'), 1, "σ"},
+    { V('*','t'), 1, "τ"},
+    { V('*','u'), 1, "υ"},
+    { V('*','w'), 1, "ω"},
+    { V('*','x'), 1, "χ"},
+    { V('*','y'), 1, "η"},
+    { V('*','z'), 1, "ζ"},
+    { V('+','-'), 1, "±" }, // not in groff_char(7)
+    { V('+','f'), 1, "φ"}, // phi1, we use the standard phi
+    { V('+','h'), 1, "θ"}, // theta1, we use the standard theta
+    { V('+','p'), 1, "ω"}, // omega1, we use the standard omega
+    { V('1','2'), 1, "½" },
+    { V('1','4'), 1, "¼" },
+    { V('3','4'), 1, "¾" },
+    { V('F','i'), 1, "ffi" }, // ffi ligature
+    { V('F','l'), 1, "ffl" }, // ffl ligature
+    { V('a','p'), 1, "~" },
+    { V('b','r'), 1, "|" },
+    { V('b','u'), 1, "•" },
+    { V('b','v'), 1, "|" },
+    { V('c','i'), 1, "○" }, // circle ### TODO verify
+    { V('c','o'), 1, "©" },
+    { V('c','t'), 1, "¢" },
+    { V('d','e'), 1, "°" },
+    { V('d','g'), 1, "†" },
+    { V('d','i'), 1, "÷" },
+    { V('e','m'), 1, "&emdash;" },
+    { V('e','n'), 1, "&endash;"},
+    { V('e','q'), 1, "=" },
+    { V('e','s'), 1, "∅" },
+    { V('f','f'), 1, "�xFB00;" }, // ff ligature
+    { V('f','i'), 1, "�xFB01;" }, // fi ligature
+    { V('f','l'), 1, "�xFB02;" }, // fl ligature
+    { V('f','m'), 1, "′" },
+    { V('g','a'), 1, "`" },
+    { V('h','y'), 1, "-" },
+    { V('l','c'), 2, "|¯" }, // ### TODO: not in groff_char(7)
+    { V('l','f'), 2, "|_" }, // ### TODO: not in groff_char(7)
+    { V('l','k'), 1, "{" }, // ### TODO: not in groff_char(7)
+    { V('m','i'), 1, "-" }, // ### TODO: not in groff_char(7)
+    { V('m','u'), 1, "×" },
+    { V('n','o'), 1, "¬" },
+    { V('o','r'), 1, "|" },
+    { V('p','l'), 1, "+" },
+    { V('r','c'), 2, "¯|" }, // ### TODO: not in groff_char(7)
+    { V('r','f'), 2, "_|" },  // ### TODO: not in groff_char(7)
+    { V('r','g'), 1, "®" },
+    { V('r','k'), 1, "}" }, // ### TODO: not in groff_char(7)
+    { V('r','n'), 1, "‾" },
+    { V('r','u'), 1, "_" },
+    { V('s','c'), 1, "§" },
+    { V('s','l'), 1, "/" },
+    { V('s','q'), 2, "□" }, // WHITE SQUARE
+    { V('t','s'), 1, "ς" }, // FINAL SIGMA
+    { V('u','l'), 1, "_" },
+    { V('-','D'), 1, "Ð" },
+    { V('S','d'), 1, "ð" },
+    { V('T','P'), 1, "Þ" },
+    { V('T','p'), 1, "þ" },
+    { V('A','E'), 1, "Æ" },
+    { V('a','e'), 1, "æ" },
+    { V('O','E'), 1, "Œ" },
+    { V('o','e'), 1, "œ" },
+    { V('s','s'), 1, "ß" },
+    { V('\'','A'), 1, "Á" },
+    { V('\'','E'), 1, "É" },
+    { V('\'','I'), 1, "Í" },
+    { V('\'','O'), 1, "Ó" },
+    { V('\'','U'), 1, "Ú" },
+    { V('\'','Y'), 1, "Ý" },
+    { V('\'','a'), 1, "á" },
+    { V('\'','e'), 1, "é" },
+    { V('\'','i'), 1, "í" },
+    { V('\'','o'), 1, "ó" },
+    { V('\'','u'), 1, "ú" },
+    { V('\'','y'), 1, "ý" },
+    { V(':','A'), 1, "Ä" },
+    { V(':','E'), 1, "Ë" },
+    { V(':','I'), 1, "Ï" },
+    { V(':','O'), 1, "Ö" },
+    { V(':','U'), 1, "Ü" },
+    { V(':','a'), 1, "ä" },
+    { V(':','e'), 1, "ë" },
+    { V(':','i'), 1, "ï" },
+    { V(':','o'), 1, "ö" },
+    { V(':','u'), 1, "ü" },
+    { V(':','y'), 1, "ÿ" },
+    { V('^','A'), 1, "Â" },
+    { V('^','E'), 1, "Ê" },
+    { V('^','I'), 1, "Î" },
+    { V('^','O'), 1, "Ô" },
+    { V('^','U'), 1, "Û" },
+    { V('^','a'), 1, "â" },
+    { V('^','e'), 1, "ê" },
+    { V('^','i'), 1, "î" },
+    { V('^','o'), 1, "ô" },
+    { V('^','u'), 1, "û" },
+    { V('`','A'), 1, "À" },
+    { V('`','E'), 1, "È" },
+    { V('`','I'), 1, "Ì" },
+    { V('`','O'), 1, "Ò" },
+    { V('`','U'), 1, "Ù" },
+    { V('`','a'), 1, "à" },
+    { V('`','e'), 1, "è" },
+    { V('`','i'), 1, "ì" },
+    { V('`','o'), 1, "ò" },
+    { V('`','u'), 1, "ù" },
+    { V('~','A'), 1, "Ã" },
+    { V('~','N'), 1, "Ñ" },
+    { V('~','O'), 1, "Õ" },
+    { V('~','a'), 1, "ã" },
+    { V('~','n'), 1, "&ntidle;" },
+    { V('~','o'), 1, "&otidle;" },
+    { V(',','C'), 1, "Ç" },
+    { V(',','c'), 1, "ç" },
+    { V('/','L'), 1, "Ł" },
+    { V('/','l'), 1, "ł" },
+    { V('/','O'), 1, "Ø" },
+    { V('/','o'), 1, "ø" },
+    { V('o','A'), 1, "Å" },
+    { V('o','a'), 1, "å" },
+    { V('a','"'), 1, "\"" },
+    { V('a','-'), 1, "¯" },
+    { V('a','.'), 1, "." },
+    { V('a','^'), 1, "ˆ" },
+    { V('a','a'), 1, "´" },
+    { V('a','b'), 1, "`" },
+    { V('a','c'), 1, "¸" },
+    { V('a','d'), 1, "¨" },
+    { V('a','h'), 1, "˂" }, // caron
+    { V('a','o'), 1, "˚" }, // ring
+    { V('a','~'), 1, "˜" },
+    { V('h','o'), 1, "˛" }, // ogonek
+    { V('.','i'), 1, "ı" }, // dot less i
+    { V('C','s'), 1, "¤" },
+    { V('D','o'), 1, "$" },
+    { V('P','o'), 1, "£" },
+    { V('Y','e'), 1, "¥" },
+    { V('F','n'), 1, "ƒ" },
+    { V('F','o'), 1, "«" },
+    { V('F','c'), 1, "»" },
+    { V('f','o'), 1, "‹" }, // single left guillemet
+    { V('f','c'), 1, "›" }, // single right guillemet
+    { V('r','!'), 1, "&iecl;" },
+    { V('r','?'), 1, "¿" },
+    { V('O','f'), 1, "ª" },
+    { V('O','m'), 1, "º" },
+    { V('p','c'), 1, "·" },
+    { V('S','1'), 1, "¹" },
+    { V('S','2'), 1, "²" },
+    { V('S','3'), 1, "³" },
+    { V('<','-'), 1, "←" },
+    { V('-','>'), 1, "→" },
+    { V('<','>'), 1, "↔" },
+    { V('d','a'), 1, "↓" },
+    { V('u','a'), 1, "↑" },
+    { V('l','A'), 1, "⇐" },
+    { V('r','A'), 1, "⇒" },
+    { V('h','A'), 1, "⇔" },
+    { V('d','A'), 1, "⇓" },
+    { V('u','A'), 1, "⇑" },
+    { V('b','a'), 1, "|" },
+    { V('b','b'), 1, "¦" },
+    { V('t','m'), 1, "™" },
+    { V('d','d'), 1, "‡" },
+    { V('p','s'), 1, "¶" },
+    { V('%','0'), 1, "‰" },
+    { V('f','/'), 1, "⁄" }, // Fraction slash
+    { V('s','d'), 1, "″" },
+    { V('h','a'), 1, "^" },
+    { V('t','i'), 1, "&tidle;" },
+    { V('l','B'), 1, "[" },
+    { V('r','B'), 1, "]" },
+    { V('l','C'), 1, "{" },
+    { V('r','C'), 1, "}" },
+    { V('l','a'), 1, "<" },
+    { V('r','a'), 1, ">" },
+    { V('l','h'), 1, "≤" },
+    { V('r','h'), 1, "≥" },
+    { V('B','q'), 1, "„" },
+    { V('b','q'), 1, "‚" },
+    { V('l','q'), 1, "“" },
+    { V('r','q'), 1, "”" },
+    { V('o','q'), 1, "‘" },
+    { V('c','q'), 1, "’" },
+    { V('a','q'), 1, "'" },
+    { V('d','q'), 1, "\"" },
+    { V('a','t'), 1, "@" },
+    { V('s','h'), 1, "#" },
+    { V('r','s'), 1, "\\" },
+    { V('t','f'), 1, "∴" },
+    { V('~','~'), 1, "≅" },
+    { V('~','='), 1, "≈" },
+    { V('!','='), 1, "≠" },
+    { V('<','='), 1, "≤" },
+    { V('=','='), 1, "≡" },
+    { V('=','~'), 1, "≅" }, // ### TODO: verify
+    { V('>','='), 1, "≥" },
+    { V('A','N'), 1, "∧" },
+    { V('O','R'), 1, "∨" },
+    { V('t','e'), 1, "∃" },
+    { V('f','a'), 1, "∀" },
+    { V('A','h'), 1, "ℵ" },
+    { V('I','m'), 1, "ℑ" },
+    { V('R','e'), 1, "ℜ" },
+    { V('i','f'), 1, "∞" },
+    { V('m','d'), 1, "⋅" },
+    { V('m','o'), 1, "∆" }, // element ### TODO verify
+    { V('n','m'), 1, "∉" },
+    { V('p','t'), 1, "∝" },
+    { V('p','p'), 1, "⊥" },
+    { V('s','b'), 1, "⊂" },
+    { V('s','p'), 1, "⊃" },
+    { V('i','b'), 1, "⊆" },
+    { V('i','p'), 1, "⊇" },
+    { V('i','s'), 1, "∫" },
+    { V('s','r'), 1, "√" },
+    { V('p','d'), 1, "∂" },
+    { V('c','*'), 1, "⊗" },
+    { V('c','+'), 1, "⊕" },
+    { V('c','a'), 1, "∩" },
+    { V('c','u'), 1, "∪" },
+    { V('g','r'), 1, "V" }, // gradient ### TODO Where in Unicode?
+    { V('C','R'), 1, "↵" },
+    { V('s','t'), 2, "-)" }, // "such that" ### TODO Where in Unicode?
+    { V('/','_'), 1, "∠" },
+    { V('w','p'), 1, "℘" },
+    { V('l','z'), 1, "◊" },
+    { V('a','n'), 1, "-" }, // "horizontal arrow extension"  ### TODO Where in Unicode?
+};
+
+/* default: print code */
+
+
+/* static char eqndelimopen=0, eqndelimclose=0; */
+static char escapesym='\\', nobreaksym='\'', controlsym='.', fieldsym=0, padsym=0;
+
+static char *buffer=NULL;
+static int buffpos=0, buffmax=0;
+static bool scaninbuff=false;
+static int itemdepth=0;
+static int section=0;
+static int dl_set[20]= { 0 };
+static bool still_dd=0;
+static int tabstops[20] = { 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96 };
+static int maxtstop=12;
+static int curpos=0;
+
+static char *scan_troff(char *c, bool san, char **result);
+static char *scan_troff_mandoc(char *c, bool san, char **result);
+
+static TQValueList s_argumentList;
+
+static TQCString htmlPath, cssPath;
+
+static TQCString s_dollarZero; // Value of $0
+
+void setResourcePath(const TQCString& _htmlPath, const TQCString& _cssPath)
+{
+    htmlPath=_htmlPath;
+    cssPath=_cssPath;
+}
+
+static void fill_old_character_definitions( void )
+{
+    for (size_t i = 0; i < sizeof(standardchar)/sizeof(CSTRDEF); i++)
+    {
+        const int nr = standardchar[i].nr;
+        const char temp[3] = { nr / 256, nr % 256, 0 };
+        TQCString name( temp );
+        s_characterDefinitionMap.insert( name, StringDefinition( standardchar[i].slen, standardchar[i].st ) );
+    }
+}
+
+static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
+static int no_newline_output=0;
+static int newline_for_fun=0;
+static bool output_possible=false;
+
+static const char *includedirs[] = {
+    "/usr/include",
+    "/usr/include/sys",
+    "/usr/local/include",
+    "/opt/local/include",
+    "/usr/ccs",
+    "/usr/X11R6/include",
+    "/usr/openwin/include",
+    "/usr/include/g++",
+    0
+};
+
+static bool ignore_links=false;
+
+static void add_links(char *c)
+{
+    /*
+    ** Add the links to the output.
+    ** At the moment the following are recognized:
+    **
+    ** name(*)                 -> ../man?/name.*
+    ** method://string         -> method://string
+    ** www.host.name           -> http://www.host.name
+    ** ftp.host.name           -> ftp://ftp.host.name
+    ** name@host               -> mailto:name@host
+    **                 -> file:/usr/include/name.h   (guess)
+    **
+    ** Other possible links to add in the future:
+    **
+    ** /dir/dir/file  -> file:/dir/dir/file
+    */
+    if (ignore_links)
+    {
+        output_real(c);
+        return;
+    }
+
+    int i,j,nr;
+    char *f, *h;
+    char *g;
+    const int numtests=6; // Nmber of tests
+    char *idtest[numtests]; // url, mailto, www, ftp, manpage, C header file
+    bool ok;
+    /* search for (section) */
+    nr=0;
+    idtest[0]=strstr(c+1,"://");
+    idtest[1]=strchr(c+1,'@');
+    idtest[2]=strstr(c,"www.");
+    idtest[3]=strstr(c,"ftp.");
+    idtest[4]=strchr(c+1,'(');
+    idtest[5]=strstr(c+1,".h>");
+    for (i=0; i */
+	    f=idtest[5];
+	    h=f+2;
+	    g=f;
+	    while (g>c && g[-1]!=';') g--;
+            bool wrote_include = false;
+
+            if (g!=c) {
+
+                TQCString dir;
+                TQCString file(g, h - g + 1);
+                file = file.stripWhiteSpace();
+                for (int index = 0; includedirs[index]; index++) {
+                    TQCString str = TQCString(includedirs[index]) + "/" + file;
+                    if (!access(str, R_OK)) {
+                        dir = includedirs[index];
+                        break;
+                    }
+                }
+                if (!dir.isEmpty()) {
+
+                    char t;
+                    t=*g;
+                    *g=0;
+                    output_real(c);
+                    *g=t;*h=0;
+
+                    TQCString str;
+                    str.sprintf("%s>", dir.data(), file.data(), file.data());
+                    output_real(str.data());
+                    c=f+6;
+                    wrote_include = true;
+                }
+
+            }
+
+            if (!wrote_include) {
+                f[5]=0;
+                output_real(c);
+                f[5]=';';
+                c=f+5;
+            }
+        }
+        break;
+	case 4: /* manpage */
+	    f=idtest[j];
+	    /* check section */
+	    g=strchr(f,')');
+            // The character before f must alphanumeric, the end of a HTML tag or the end of a  
+            if (g!=NULL && f>c && (g-f)<12 && (isalnum(f[-1]) || f[-1]=='>' || ( f[-1] == ';' ) ) &&
+		isdigit(f[1]) && f[1]!='0' && ((g-f)<=2 || isalpha(f[2])))
+	    {
+		ok = TRUE;
+		h = f+2;
+		while (h c + 5 ) && ( ! memcmp( h-5, " ", 6 ) ) )
+                {
+                    h -= 6;
+                    kdDebug(7107) << "Skip  " << endl;
+                }
+                else if ( *h == ';' )
+                {
+                    // Not a non-breaking space, so probably not ok
+                    ok = false;
+                }
+            }
+            
+	    if (ok)
+	    {
+		/* this might be a link */
+		/* skip html makeup */
+		while (h>c && *h=='>') {
+		    while (h!=c && *h!='<') h--;
+		    if (h!=c) h--;
+		}
+		if (isalnum(*h)) {
+		    char t,sec, *e;
+                    TQString subsec; // ### TODO avoid using TQString, as we do not know the encoding
+                    TQString fstr(f); // ### TODO avoid using TQString, as we do not know the encoding
+		    e=h+1;
+		    sec=f[1];
+		    subsec=f[2];
+		    int index = fstr.find(')', 2);
+		    if (index != -1)
+		      subsec = fstr.mid(2, index - 2);
+		    else // No closing ')' found, take first character as subsection.
+		      subsec = fstr.mid(2, 1);
+		    while (h>c && (isalnum(h[-1]) || h[-1]=='_'
+				    || h[-1]==':' || h[-1]=='-' || h[-1]=='.'))
+			h--;
+		    t=*h;
+		    *h='\0';
+                    output_real(c);
+		    *h=t;
+		    t=*e;
+		    *e='\0';
+                    TQCString str;
+		    if (subsec.isEmpty())
+                        str.sprintf("%s", h, sec, h);
+		    else
+                        str.sprintf("%s", h, sec, subsec.lower().latin1(), h);
+                    output_real(str.data());
+		    *e=t;
+		    c=e;
+		}
+	    }
+	    *f='\0';
+            output_real(c);
+	    *f='(';
+	    idtest[4]=f-1;
+	    c=f;
+	    break; /* manpage */
+	case 3: /* ftp */
+	case 2: /* www */
+	    g=f=idtest[j];
+	    while (*g && (isalnum(*g) || *g=='_' || *g=='-' || *g=='+' ||
+                *g=='.' || *g=='/')) g++;
+	    if (g[-1]=='.') g--;
+	    if (g-f>4) {
+		char t;
+		t=*f; *f='\0';
+                output_real(c);
+		*f=t; t=*g;*g='\0';
+                TQCString str;
+                str.sprintf("%s", ((j==3)?"ftp":"http"), f, f);
+                output_real(str.data());
+		*g=t;
+		c=g;
+	    } else {
+		f[3]='\0';
+                output_real(c);
+		c=f+3;
+		f[3]='.';
+	    }
+	    break;
+	case 1: /* mailto */
+	    g=f=idtest[1];
+	    while (g>c && (isalnum(g[-1]) || g[-1]=='_' || g[-1]=='-' ||
+			   g[-1]=='+' || g[-1]=='.' || g[-1]=='%')) g--;
+            if (g-7>=c && g[-1]==':')
+            {
+                // We have perhaps an email address starting with mailto:
+                if (!tqstrncmp("mailto:",g-7,7))
+                    g-=7;
+            }
+	    h=f+1;
+	    while (*h && (isalnum(*h) || *h=='_' || *h=='-' || *h=='+' ||
+			  *h=='.')) h++;
+	    if (*h=='.') h--;
+	    if (h-f>4 && f-g>1) {
+		char t;
+		t=*g;
+		*g='\0';
+                output_real(c);
+		*g=t;t=*h;*h='\0';
+                TQCString str;
+                str.sprintf("%s", g, g);
+                output_real(str.data());
+		*h=t;
+                c=h;
+	    } else {
+		*f='\0';
+                output_real(c);
+		*f='@';
+		idtest[1]=c;
+		c=f;
+	    }
+	    break;
+	case 0: /* url */
+	    g=f=idtest[0];
+	    while (g>c && isalpha(g[-1]) && islower(g[-1])) g--;
+	    h=f+3;
+	    while (*h && !isspace(*h) && *h!='<' && *h!='>' && *h!='"' &&
+		   *h!='&') h++;
+	    if (f-g>2 && f-g<7 && h-f>3) {
+		char t;
+		t=*g;
+		*g='\0';
+                output_real(c);
+		*g=t; t=*h; *h='\0';
+                TQCString str;
+                str.sprintf("%s", g, g);
+                output_real(str.data());
+		*h=t;
+		c=h;
+	    } else {
+		f[1]='\0';
+                output_real(c);
+		f[1]='/';
+		c=f+1;
+	    }
+	    break;
+     default:
+	    break;
+	}
+	nr=0;
+	if (idtest[0] && idtest[0]<=c) idtest[0]=strstr(c+1,"://");
+	if (idtest[1] && idtest[1]<=c) idtest[1]=strchr(c+1,'@');
+	if (idtest[2] && idtest[2]=buffmax) {
+	      char *h = new char[buffmax*2];
+       
+#ifdef SIMPLE_MAN2HTML
+	      if (!h)
+              {
+                  cerr << "Memory full, cannot output!" << endl;
+                  exit(1);
+              }
+#else
+// modern compiler do not return a NULL for a new
+#endif       
+              memcpy(h, buffer, buffmax);
+              delete [] buffer;
+	      buffer=h;
+	      buffmax=buffmax*2;
+	  }
+	  buffer[buffpos++]=*c2++;
+      }
+  } else
+      if (output_possible) {
+	  while (*c2) {
+	      outbuffer[obp++]=*c2;
+	      if (*c=='\n' || obp >= HUGE_STR_MAX) {
+		  outbuffer[obp]='\0';
+		  add_links(outbuffer);
+		  obp=0;
+	      }
+	      c2++;
+	  }
+      }
+  delete [] c3;
+}
+
+static TQCString set_font( const TQCString& name )
+{
+    // Every font but R (Regular) creates  elements
+    TQCString markup;
+    if ( current_font != "R" && !current_font.isEmpty() )
+        markup += "";
+    const uint len = name.length();
+    bool fontok = true;
+    if ( len == 1 )
+    {
+        const char lead = name[0];
+        switch (lead)
+        {
+            case 'P': // Palatino?
+            case 'R': break; // regular, do nothing
+            case 'I': markup += ""; break;
+            case 'B': markup += ""; break;
+            case 'L': markup += ""; break; // ### What's L?
+            default: fontok = false;
+        }
+    }
+    else if ( len == 2 )
+    {
+        if ( name == "BI" )
+            markup += "";
+        // Courier
+        else if ( name == "CR" )
+            markup += "";
+        else if ( name == "CW" ) // CW is used by pod2man(1) (alias PerlDoc)
+            markup += "";
+        else if ( name == "CI" )
+            markup += "";
+        else if ( name == "CB" )
+            markup += "";
+        // Times
+        else if ( name == "TR" )
+            markup += "";
+        else if ( name == "TI" )
+            markup += "";
+        else if ( name == "TB" )
+            markup += "";
+        // Helvetica
+        else if ( name == "HR" )
+            markup += "";
+        else if ( name == "HI" )
+            markup += "";
+        else if ( name == "HB" )
+            markup += "";
+        else
+            fontok = false;
+    }
+    else if ( len == 3 )
+    {
+        if ( name == "CBI" )
+            markup += "";
+        else if ( name == "TBI" )
+            markup += "";
+        else if ( name == "HBI" )
+            markup += "";
+    }
+    if (fontok)
+        current_font = name;
+    else
+        current_font = "R"; // Still nothing, then it is 'R' (Regular)
+    return markup;
+}
+
+/// \deprecated
+static TQCString set_font( const char ch )
+#ifndef SIMPLE_MAN2HTML
+        KDE_DEPRECATED;
+
+static TQCString set_font( const char ch )
+#endif
+{
+    const TQCString name = &ch;
+    return set_font( name );
+}
+
+static TQCString change_to_size(int nr)
+{
+    switch (nr)
+    {
+        case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+        case '7': case '8': case '9': nr=nr-'0'; break;
+        case '\0': break;
+        default: nr=current_size+nr; if (nr>9) nr=9; if (nr< -9) nr=-9; break;
+    }
+    if ( nr == current_size )
+        return "";
+    const TQCString font ( current_font );
+    TQCString markup;
+    markup = set_font("R");
+    if (current_size)
+        markup += "";
+    current_size=nr;
+    if (nr)
+    {
+        markup += "0)
+            markup += '+';
+        else
+        {
+            markup += '-';
+            nr=-nr;
+        }
+        markup += char( nr + '0' );
+        markup += "\">";
+    }
+    markup += set_font( font );
+    return markup;
+}
+
+/* static int asint=0; */
+static int intresult=0;
+
+#define SKIPEOL while (*c && *c++!='\n')
+
+static bool skip_escape=false;
+static bool single_escape=false;
+
+static char *scan_escape_direct( char *c, TQCString& cstr );
+
+/**
+ * scan a named character
+ * param c position
+*/
+static TQCString scan_named_character( char*& c )
+{
+    TQCString name;
+    if ( *c == '(' )
+    {
+        // \*(ab  Name of two characters
+        if ( c[1] == escapesym )
+        {
+            TQCString cstr;
+            c = scan_escape_direct( c+2, cstr );
+            // ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
+            name = cstr;
+        }
+        else
+        {
+            name+=c[1];
+            name+=c[2];
+            c+=3;
+        }
+    }
+    else if ( *c == '[' )
+    {
+        // \*[long_name]  Long name
+        // Named character groff(7)
+        // We must find the ] to get a name
+        c++;
+        while ( *c && *c != ']' && *c != '\n' )
+        {
+            if ( *c == escapesym )
+            {
+                TQCString cstr;
+                c = scan_escape_direct( c+1, cstr );
+                const int result = cstr.find(']');
+                if ( result == -1 )
+                    name += cstr;
+                else
+                {
+                    // Note: we drop the characters after the ]
+                    name += cstr.left( result );
+                }
+            }
+            else
+            {
+                name+=*c;
+                c++;
+            }
+        }
+        if ( !*c || *c == '\n' )
+        {
+            kdDebug(7107) << "Found linefeed! Could not parse character name: " << name << endl;
+            return "";
+        }
+        c++;
+    }
+    else if ( *c =='C' || c[1]== '\'' )
+    {
+        // \C'name'
+        c+=2;
+        while ( *c && *c != '\'' && *c != '\n' )
+        {
+            if ( *c == escapesym )
+            {
+                TQCString cstr;
+                c = scan_escape_direct( c+1, cstr );
+                const int result = cstr.find('\'');
+                if ( result == -1 )
+                    name += cstr;
+                else
+                {
+                    // Note: we drop the characters after the ]
+                    name += cstr.left( result );
+                }
+            }
+            else
+            {
+                name+=*c;
+                c++;
+            }
+        }
+        if ( !*c || *c == '\n' )
+        {
+            kdDebug(7107) << "Found linefeed! Could not parse (\\C mode) character name: " << name << endl;
+            return "";
+        }
+        c++;
+    }
+    // Note: characters with a one character length name doe not exist, as they would collide with other escapes
+    
+    // Now we have the name, let us find it between the string names
+    TQMap::iterator it=s_characterDefinitionMap.find(name);
+    if (it==s_characterDefinitionMap.end())
+    {
+        kdDebug(7107) << "EXCEPTION: cannot find character with name: " << name << endl;
+        // No output, as an undefined string is empty by default
+        return "";
+    }
+    else
+    {
+        kdDebug(7107) << "Character with name: \"" << name << "\" => " << (*it).m_output << endl;
+        return (*it).m_output;
+    }
+}
+
+static TQCString scan_named_string(char*& c)
+{
+    TQCString name;
+    if ( *c == '(' )
+    {
+        // \*(ab  Name of two characters
+        if ( c[1] == escapesym )
+        {
+            TQCString cstr;
+            c = scan_escape_direct( c+2, cstr );
+            kdDebug(7107) << "\\(" << cstr << endl;
+            // ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
+            name = cstr;
+        }
+        else
+        {
+            name+=c[1];
+            name+=c[2];
+            c+=3;
+        }
+    }
+    else if ( *c == '[' )
+    {
+        // \*[long_name]  Long name
+        // Named character groff(7)
+        // We must find the ] to get a name
+        c++;
+        while ( *c && *c != ']' && *c != '\n' )
+        {
+            if ( *c == escapesym )
+            {
+                TQCString cstr;
+                c = scan_escape_direct( c+1, cstr );
+                const int result = cstr.find(']');
+                if ( result == -1 )
+                    name += cstr;
+                else
+                {
+                    // Note: we drop the characters after the ]
+                    name += cstr.left( result );
+                }
+            }
+            else
+            {
+                name+=*c;
+                c++;
+            }
+        }
+        if ( !*c || *c == '\n' )
+        {
+            kdDebug(7107) << "Found linefeed! Could not parse string name: " << name << endl;
+            return "";
+        }
+        c++;
+    }
+    else
+    {
+        // \*a Name of one character
+        name+=*c;
+        c++;
+    }
+    // Now we have the name, let us find it between the string names
+    TQMap::iterator it=s_stringDefinitionMap.find(name);
+    if (it==s_stringDefinitionMap.end())
+    {
+        kdDebug(7107) << "EXCEPTION: cannot find string with name: " << name << endl;
+        // No output, as an undefined string is empty by default
+        return "";
+    }
+    else
+    {
+        kdDebug(7107) << "String with name: \"" << name << "\" => " << (*it).m_output << endl;
+        return (*it).m_output;
+    }
+}
+
+static TQCString scan_dollar_parameter(char*& c)
+{
+    unsigned int argno = 0; // No dollar argument number yet!
+    if ( *c == '0' )
+    {
+        //kdDebug(7107) << "$0" << endl;
+        c++;
+        return s_dollarZero;
+    }
+    else if ( *c >= '1' && *c <= '9' )
+    {
+        //kdDebug(7107) << "$ direct" << endl;
+        argno = ( *c - '0' );
+        c++;
+    }
+    else if ( *c == '(' )
+    {
+        //kdDebug(7107) << "$(" << endl;
+        if ( c[1] && c[2] && c[1] >= '0' && c[1] <= '9' && c[2] >= '0' && c[2] <= '9' )
+        {
+            argno = ( c[1] - '0' ) * 10 + ( c[2] - '0' );
+            c += 3;
+        }
+        else
+        {
+            if ( !c[1] )
+                c++;
+            else if ( !c[2] )
+                c+=2;
+            else
+                c += 3;
+            return "";
+        }
+    }
+    else if ( *c == '[' )
+    {
+        //kdDebug(7107) << "$[" << endl;
+        argno = 0;
+        c++;
+        while ( *c && *c>='0' && *c<='9' && *c!=']' )
+        {
+            argno *= 10;
+            argno += ( *c - '0' );
+            c++;
+        }
+        if ( *c != ']' )
+        {
+            return "";
+        }
+        c++;
+    }
+    else if ( ( *c == '*' ) || ( *c == '@' ) )
+    {
+        const bool quote = ( *c == '@' );
+        TQValueList::const_iterator it = s_argumentList.begin();
+        TQCString param;
+        bool space = false;
+        for ( ; it != s_argumentList.end(); ++it )
+        {
+            if (space)
+                param += " ";
+            if (quote)
+                param += '\"'; // Not as HTML, as it could be used by macros !
+            param += (*it);
+            if (quote)
+                param += '\"'; // Not as HTML, as it could be used by macros!
+            space = true;
+        }
+        c++;
+        return param;
+    }
+    else
+    {
+            kdDebug(7107) << "EXCEPTION: unknown parameter $" << *c << endl;
+        return "";
+    }
+    //kdDebug(7107) << "ARG $" << argno << endl;
+    if ( !s_argumentList.isEmpty() && argno > 0 )
+    {
+        //kdDebug(7107) << "ARG $" << argno << " OK!" << endl;
+        argno--;
+        if ( argno >= s_argumentList.size() )
+        {
+            kdDebug(7107) << "EXCEPTION: cannot find parameter $" << (argno+1) << endl;
+            return "";
+        }
+
+        return s_argumentList[argno];
+    }
+    return "";
+}
+
+/// return the value of read-only number registers
+static int read_only_number_register( const TQCString& name )
+{
+    // Internal read-only variables
+    if ( name == ".$" )
+    {
+        kdDebug(7107) << "\\n[.$] == " << s_argumentList.size() << endl;
+        return s_argumentList.size();
+    }
+    else if ( name == ".g" )
+        return 0; // We are not groff(1)
+    else if ( name == ".s" )
+        return current_size;
+#if 0
+    // ### TODO: map the fonts to a number
+    else if ( name == ".f" )
+        return current_font;
+#endif
+    else if ( name == ".P" )
+        return 0; // We are not printing
+    else if ( name == ".A" )
+        return s_nroff;
+#ifndef SIMPLE_MAN2HTML
+    // Special KDE KIO man:
+    else if ( name == ".TDE_VERSION_MAJOR" )
+        return TDE_VERSION_MAJOR;
+    else if ( name == ".TDE_VERSION_MINOR" )
+        return TDE_VERSION_MINOR;
+    else if ( name == ".TDE_VERSION_RELEASE" )
+        return TDE_VERSION_RELEASE;
+    else if ( name == ".TDE_VERSION" )
+        return TDE_VERSION;
+#endif
+    // ### TODO: should .T be set to "html"? But we are not the HTML post-processor. :-(
+
+    // ### TODO: groff defines much more read-only number registers
+#ifndef SIMPLE_MAN2HTML
+            kdDebug(7107) << "EXCEPTION: unknown read-only number register: " << name << endl;
+#endif
+    
+    return 0; // Undefined variable
+
+}
+
+/// get the value of a number register and auto-increment if asked
+static int scan_number_register( char*& c)
+{
+    int sign = 0; // Sign for auto-increment (if any)
+    switch (*c)
+    {
+        case '+': sign = 1; c++; break;
+        case '-': sign = -1; c++; break;
+        default: break;
+    }
+    TQCString name;
+    if ( *c == '[' )
+    {
+        c++;
+        if ( *c == '+' )
+        {
+            sign = 1;
+            c++;
+        }
+        else if ( *c == '-' )
+        {
+            sign = -1;
+            c++;
+        }
+        while ( *c && *c != ']' && *c != '\n' )
+        {
+            // ### TODO: a \*[string] could be inside and should be processed
+            name+=*c;
+            c++;
+        }
+        if ( !*c || *c == '\n' )
+        {
+            kdDebug(7107) << "Found linefeed! Could not parse number register name: " << name << endl;
+            return 0;
+        }
+        c++;
+    }
+    else if ( *c == '(' )
+    {
+        c++;
+        if ( *c == '+' )
+        {
+            sign = 1;
+            c++;
+        }
+        else if ( *c == '-' )
+        {
+            sign = -1;
+            c++;
+        }
+        name+=c[0];
+        name+=c[1];
+        c+=2;
+    }
+    else
+    {
+        name += *c;
+        c++;
+    }
+    if ( name[0] == '.' )
+    {
+        return read_only_number_register( name );
+    }
+    else
+    {
+        TQMap< TQCString, NumberDefinition >::iterator it = s_numberDefinitionMap.find( name );
+        if ( it == s_numberDefinitionMap.end() )
+        {
+            return 0; // Undefined variable
+        }
+        else
+        {
+            (*it).m_value += sign * (*it).m_increment;
+            return (*it).m_value;
+        }
+    }
+}
+
+/// get and set font
+static TQCString scan_named_font( char*& c )
+{
+    TQCString name;
+    if ( *c == '(' )
+    {
+        // \f(ab  Name of two characters
+        if ( c[1] == escapesym )
+        {
+            TQCString cstr;
+            c = scan_escape_direct( c+2, cstr );
+            kdDebug(7107) << "\\(" << cstr << endl;
+            // ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
+            name = cstr;
+        }
+        else
+        {
+            name+=c[1];
+            name+=c[2];
+            c+=3;
+        }
+    }
+    else if ( *c == '[' )
+    {
+        // \f[long_name]  Long name
+        // We must find the ] to get a name
+        c++;
+        while ( *c && *c != ']' && *c != '\n' )
+        {
+            if ( *c == escapesym )
+            {
+                TQCString cstr;
+                c = scan_escape_direct( c+1, cstr );
+                const int result = cstr.find(']');
+                if ( result == -1 )
+                    name += cstr;
+                else
+                {
+                    // Note: we drop the characters after the ]
+                    name += cstr.left( result );
+                }
+            }
+            else
+            {
+                name+=*c;
+                c++;
+            }
+        }
+        if ( !*c || *c == '\n' )
+        {
+            kdDebug(7107) << "Found linefeed! Could not parse font name: " << name << endl;
+            return "";
+        }
+        c++;
+    }
+    else
+    {
+        // \fa Font name with one character or one digit
+        // ### HACK do *not* use:  name = *c;  or name would be empty
+        name += *c;
+        c++;
+    }
+    //kdDebug(7107) << "FONT NAME: " << name << endl;
+    // Now we have the name, let us find the font
+    bool ok = false;
+    const unsigned int number = name.toUInt( &ok );
+    if ( ok )
+    {
+        if ( number < 5 )
+        {
+            const char* fonts[] = { "R", "I", "B", "BI", "CR" }; // Regular, Italic, Bold, Bold Italic, Courier regular
+            name = fonts[ number ];
+        }
+        else
+        {
+            kdDebug(7107) << "EXCEPTION: font has too big number: " << name << " => " << number << endl;
+            name = "R"; // Let assume Regular
+        }
+    }
+    else if ( name.isEmpty() )
+    {
+        kdDebug(7107) << "EXCEPTION: font has no name: " << name << endl;
+        name = "R"; // Let assume Regular
+    }
+    if ( !skip_escape )
+        return set_font( name );
+    else
+        return "";
+}
+
+static TQCString scan_number_code( char*& c )
+{
+    TQCString number;
+    if ( *c != '\'' )
+        return "";
+    while ( *c && ( *c != '\n' ) && ( *c != '\'' ) )
+    {
+        number += *c;
+        c++;
+    }
+    bool ok = false;
+    unsigned int result = number.toUInt( &ok );
+    if ( ( result < ' ' ) || ( result > 65535 ) )
+        return "";
+    else if ( result == '\t' )
+    {
+        curpos += 8;
+        curpos &= 0xfff8;
+        return "\t";
+    }
+    number.setNum( result );
+    number.prepend( "&#" );
+    number.append( ";" );
+    curpos ++;
+    return number;
+}
+
+// ### TODO known missing escapes from groff(7):
+// ### TODO \& \! \) \: \R
+
+static char *scan_escape_direct( char *c, TQCString& cstr )
+{
+    bool exoutputp;
+    bool exskipescape;
+    int i,j;
+    bool cplusplus = true; // Should the c++ be done at the end of the function
+
+    cstr = "";
+    intresult=0;
+    switch (*c) {
+    case 'e': cstr = "\\"; curpos++;break; // ### FIXME: it should be the current escape symbol
+    case '0': // ### TODO Where in Unicode? (space of digit width)
+    case '~': // non-breakable-space (resizeable!)
+    case ' ':
+    case '|': // half-non-breakable-space
+    case '^': // quarter-non-breakable-space
+        cstr = " "; curpos++; break;
+    case '"': SKIPEOL; c--; break;
+    // ### TODO \# like \" but does not ignore the end of line (groff(7))
+    case '$':
+    {
+        c++;
+        cstr = scan_dollar_parameter( c );
+        cplusplus = false;
+        break;
+    }
+    case 'z':
+    {
+        c++;
+        if (*c=='\\')
+        {
+            c=scan_escape_direct( c+1, cstr );
+            c--;
+        }
+        else
+            cstr = TQCString( c, 1 );
+        break;
+    }
+    case 'k': c++; if (*c=='(') c+=2; // ### FIXME \k[REG] exists too
+    case '!':
+    case '%':
+    case 'a':
+    case 'd':
+    case 'r':
+    case 'u':
+    case '\n':
+    case '&':
+        cstr = ""; break;
+    case '(':
+    case '[':
+    case 'C':
+    {
+        // Do not go forward as scan_named_character needs the leading symbol
+        cstr = scan_named_character( c );
+        cplusplus = false;
+        break;
+    }
+    case '*':
+    {
+        c++;
+        cstr = scan_named_string( c );
+        cplusplus = false;
+        break;
+    }
+    case 'f':
+    {
+        c++;
+        cstr = scan_named_font( c );
+        cplusplus = false;
+        break;
+    }
+    case 's': // ### FIXME: many forms are missing
+	c++;
+	j=0;i=0;
+	if (*c=='-') {j= -1; c++;} else if (*c=='+') {j=1; c++;}
+	if (*c=='0') c++; else if (*c=='\\') {
+	    c++;
+	    c=scan_escape_direct( c, cstr );
+	    i=intresult; if (!j) j=1;
+	} else
+	    while (isdigit(*c) && (!i || (!j && i<4))) i=i*10+(*c++)-'0';
+	if (!j) { j=1; if (i) i=i-10; }
+	if (!skip_escape) cstr=change_to_size(i*j);
+	c--;
+	break;
+    case 'n':
+    {
+        c++;
+        intresult = scan_number_register( c );
+        cplusplus = false;
+        break;
+    }
+    case 'w':
+	c++;
+	i=*c;
+	c++;
+	exoutputp=output_possible;
+	exskipescape=skip_escape;
+	output_possible=false;
+	skip_escape=true;
+	j=0;
+	while (*c!=i)
+        {
+	    j++;
+            if ( *c == escapesym )
+                c = scan_escape_direct( c+1, cstr);
+            else
+                c++;
+	}
+	output_possible=exoutputp;
+	skip_escape=exskipescape;
+	intresult=j;
+	break;
+    case 'l': cstr = "
"; curpos=0; + case 'b': + case 'v': + case 'x': + case 'o': + case 'L': + case 'h': + c++; + i=*c; + c++; + exoutputp=output_possible; + exskipescape=skip_escape; + output_possible=0; + skip_escape=true; + while (*c != i) + if (*c==escapesym) c=scan_escape_direct( c+1, cstr ); + else c++; + output_possible=exoutputp; + skip_escape=exskipescape; + break; + case 'c': no_newline_output=1; break; + case '{': newline_for_fun++; break; // Start conditional block + case '}': if (newline_for_fun) newline_for_fun--; break; // End conditional block + case 'p': cstr = "
\n";curpos=0; break; + case 't': cstr = "\t";curpos=(curpos+8)&0xfff8; break; + case '<': cstr = "<";curpos++; break; + case '>': cstr = ">";curpos++; break; + case '\\': + { + if (single_escape) + c--; + else + cstr="\\"; + break; + } + case 'N': + { + c++; + cstr = scan_number_code( c ); + cplusplus = false; + break; + } +#if 0 + { + if (*++c) c++; // c += 2 + if (sscanf(c, "%d", &i) != 1) // (### FIXME ugly!) + break; + TQCString temp; + temp.sprintf( "%d", i ); // Skip over number (### FIXME ugly!) + c += temp.length(); + switch(i) { + case 8: cstr = "\t"; curpos=(curpos+8)&0xfff8; break; + case 34: cstr = """; curpos++; break; + default: cstr = char( i ); curpos++; break; + } + break; + } +#endif + case '\'': cstr = "´";curpos++; break; // groff(7) ### TODO verify + case '`': cstr = "`";curpos++; break; // groff(7) + case '-': cstr = "-";curpos++; break; // groff(7) + case '.': cstr = ".";curpos++; break; // groff(7) + default: cstr = *c; curpos++; break; + } + if (cplusplus) + c++; + return c; +} + +static char *scan_escape(char *c) +{ + TQCString cstr; + char* result = scan_escape_direct( c, cstr ); + if ( !skip_escape ) + out_html(cstr); + return result; +} + +class TABLEROW; + +class TABLEITEM { +public: + TABLEITEM(TABLEROW *row); + ~TABLEITEM() { + delete [] contents; + } + void setContents(const char *_contents) { + delete [] contents; + contents = tqstrdup(_contents); + } + const char *getContents() const { return contents; } + + void init() { + delete [] contents; + contents = 0; + size = 0; + align = 0; + valign = 0; + colspan = 1; + rowspan = 1; + font = 0; + vleft = 0; + vright = 0; + space = 0; + width = 0; + } + + void copyLayout(const TABLEITEM *orig) { + size = orig->size; + align = orig->align; + valign = orig->valign; + colspan = orig->colspan; + rowspan = orig->rowspan; + font = orig->font; + vleft = orig->vleft; + vright = orig->vright; + space = orig->space; + width = orig->width; + } + +public: + int size,align,valign,colspan,rowspan,font,vleft,vright,space,width; + +private: + char *contents; + TABLEROW *_parent; +}; + +class TABLEROW { + char *test; +public: + TABLEROW() { + test = new char; + items.setAutoDelete(true); + prev = 0; next = 0; + } + ~TABLEROW() { + delete test; + + } + int length() const { return items.count(); } + bool has(int index) { + return (index >= 0) && (index < (int)items.count()); + } + TABLEITEM &at(int index) { + return *items.at(index); + } + + TABLEROW *copyLayout() const; + + void addItem(TABLEITEM *item) { + items.append(item); + } + TABLEROW *prev, *next; + +private: + TQPtrList items; +}; + +TABLEITEM::TABLEITEM(TABLEROW *row) : contents(0), _parent(row) { + init(); + _parent->addItem(this); +} + +TABLEROW *TABLEROW::copyLayout() const { + TABLEROW *newrow = new TABLEROW(); + + TQPtrListIterator it(items); + for ( ; it.current(); ++it) { + TABLEITEM *newitem = new TABLEITEM(newrow); + newitem->copyLayout(it.current()); + } + return newrow; +} + +static const char *tableopt[]= { "center", "expand", "box", "allbox", + "doublebox", "tab", "linesize", + "delim", NULL }; +static int tableoptl[] = { 6,6,3,6,9,3,8,5,0}; + + +static void clear_table(TABLEROW *table) +{ + TABLEROW *tr1,*tr2; + + tr1=table; + while (tr1->prev) tr1=tr1->prev; + while (tr1) { + tr2=tr1; + tr1=tr1->next; + delete tr2; + } +} + +static char *scan_expression(char *c, int *result); + +static char *scan_format(char *c, TABLEROW **result, int *maxcol) +{ + TABLEROW *layout, *currow; + TABLEITEM *curfield; + int i,j; + if (*result) { + clear_table(*result); + } + layout= currow=new TABLEROW(); + curfield=new TABLEITEM(currow); + while (*c && *c!='.') { + switch (*c) { + case 'C': case 'c': case 'N': case 'n': + case 'R': case 'r': case 'A': case 'a': + case 'L': case 'l': case 'S': case 's': + case '^': case '_': + if (curfield->align) + curfield=new TABLEITEM(currow); + curfield->align=toupper(*c); + c++; + break; + case 'i': case 'I': case 'B': case 'b': + curfield->font = toupper(*c); + c++; + break; + case 'f': case 'F': + c++; + curfield->font = toupper(*c); + c++; + if (!isspace(*c) && *c!='.') c++; + break; + case 't': case 'T': curfield->valign='t'; c++; break; + case 'p': case 'P': + c++; + i=j=0; + if (*c=='+') { j=1; c++; } + if (*c=='-') { j=-1; c++; } + while (isdigit(*c)) i=i*10+(*c++)-'0'; + if (j) curfield->size= i*j; else curfield->size=j-10; + break; + case 'v': case 'V': + case 'w': case 'W': + c=scan_expression(c+2,&curfield->width); + break; + case '|': + if (curfield->align) curfield->vleft++; + else curfield->vright++; + c++; + break; + case 'e': case 'E': + c++; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i=0; + while (isdigit(*c)) i=i*10+(*c++)-'0'; + curfield->space=i; + break; + case ',': case '\n': + currow->next=new TABLEROW(); + currow->next->prev=currow; + currow=currow->next; + currow->next=NULL; + curfield=new TABLEITEM(currow); + c++; + break; + default: + c++; + break; + } + } + if (*c=='.') while (*c++!='\n'); + *maxcol=0; + currow=layout; + while (currow) { + i=currow->length(); + if (i>*maxcol) *maxcol=i; + currow=currow->next; + } + *result=layout; + return c; +} + +static TABLEROW *next_row(TABLEROW *tr) +{ + if (tr->next) { + tr=tr->next; + if (!tr->next) + return next_row(tr); + return tr; + } else { + tr->next = tr->copyLayout(); + tr->next->prev = tr; + return tr->next; + } +} + +static char itemreset[20]="\\fR\\s0"; + +#define FORWARDCUR do { curfield++; } while (currow->has(curfield) && currow->at(curfield).align=='S'); + +static char *scan_table(char *c) +{ + char *h; + char *g; + int center=0, expand=0, box=0, border=0, linesize=1; + int i,j,maxcol=0, finished=0; + TQCString oldfont; + int oldsize,oldfillout; + char itemsep='\t'; + TABLEROW *layout=NULL, *currow; + int curfield = -1; + while (*c++!='\n'); + h=c; + if (*h=='.') return c-1; + oldfont=current_font; + oldsize=current_size; + oldfillout=fillout; + out_html(set_font("R")); + out_html(change_to_size(0)); + if (!fillout) { + fillout=1; + out_html("
"); + } + while (*h && *h!='\n') h++; + if (h[-1]==';') { + /* scan table options */ + while (cprev) { + currow->prev->next=new TABLEROW(); + currow->prev->next->next=currow; + currow->prev->next->prev=currow->prev; + currow->prev=currow->prev->next; + } else { + currow->prev=layout=new TABLEROW(); + currow->prev->prev=NULL; + currow->prev->next=currow; + } + TABLEITEM *newitem = new TABLEITEM(currow->prev); + newitem->align=*c; + newitem->colspan=maxcol; + curfield=0; + c=c+2; + } else { + if (currow->has(curfield)) { + currow->at(curfield).align=*c; + FORWARDCUR; + } + if (c[1]=='\n') { + currow=next_row(currow); + curfield=0; + } + c=c+2; + } + } else if (*c=='T' && c[1]=='{') { + h=c+2; + c=strstr(h,"\nT}"); + c++; + *c='\0'; + g=NULL; + scan_troff(h,0,&g); + scan_troff(itemreset, 0, &g); + *c='T'; + c+=3; + if (currow->has(curfield)) { + currow->at(curfield).setContents(g); + FORWARDCUR; + } + delete [] g; + + if (c[-1]=='\n') { + currow=next_row(currow); + curfield=0; + } + } else if (*c=='.' && c[1]=='T' && c[2]=='&' && c[-1]=='\n') { + TABLEROW *hr; + while (*c++!='\n'); + hr=currow; + currow=currow->prev; + hr->prev=NULL; + c=scan_format(c,&hr, &i); + hr->prev=currow; + currow->next=hr; + currow=hr; + next_row(currow); + curfield=0; + } else if (*c=='.' && c[1]=='T' && c[2]=='E' && c[-1]=='\n') { + finished=1; + while (*c++!='\n'); + if (currow->prev) + currow->prev->next=NULL; + currow->prev=NULL; + clear_table(currow); + currow = 0; + } else if (*c=='.' && c[-1]=='\n' && !isdigit(c[1])) { + /* skip troff request inside table (usually only .sp ) */ + while (*c++!='\n'); + } else { + h=c; + while (*c && (*c!=itemsep || c[-1]=='\\') && + (*c!='\n' || c[-1]=='\\')) c++; + i=0; + if (*c==itemsep) {i=1; *c='\n'; } + if (h[0]=='\\' && h[2]=='\n' && + (h[1]=='_' || h[1]=='^')) { + if (currow->has(curfield)) { + currow->at(curfield).align=h[1]; + FORWARDCUR; + } + h=h+3; + } else { + g=NULL; + h=scan_troff(h,1,&g); + scan_troff(itemreset,0, &g); + if (currow->has(curfield)) { + currow->at(curfield).setContents(g); + FORWARDCUR; + } + delete [] g; + } + if (i) *c=itemsep; + c=h; + if (c[-1]=='\n') { + currow=next_row(currow); + curfield=0; + } + } + } + /* calculate colspan and rowspan */ + currow=layout; + while (currow->next) currow=currow->next; + while (currow) { + int ti = 0, ti1 = 0, ti2 = -1; + TABLEROW *prev = currow->prev; + if (!prev) + break; + + while (prev->has(ti1)) { + if (currow->has(ti)) + switch (currow->at(ti).align) { + case 'S': + if (currow->has(ti2)) { + currow->at(ti2).colspan++; + if (currow->at(ti2).rowspanat(ti1).rowspan) + currow->at(ti2).rowspan=prev->at(ti1).rowspan; + } + break; + case '^': + if (prev->has(ti1)) prev->at(ti1).rowspan++; + default: + if (ti2 < 0) ti2=ti; + else { + do { + ti2++; + } while (currow->has(ti2) && currow->at(ti2).align=='S'); + } + break; + } + ti++; + if (ti1 >= 0) ti1++; + } + currow=currow->prev; + } + /* produce html output */ + if (center) out_html("
"); + if (box==2) out_html(""); + curfield=0; + while (currow->has(curfield)) { + if (currow->at(curfield).align!='S' && currow->at(curfield).align!='^') { + out_html("at(curfield).align) { + case 'N': + currow->at(curfield).space+=4; + case 'R': + out_html(" ALIGN=right"); + break; + case 'C': + out_html(" ALIGN=center"); + default: + break; + } + if (!currow->at(curfield).valign && currow->at(curfield).rowspan>1) + out_html(" VALIGN=center"); + if (currow->at(curfield).colspan>1) { + char buf[5]; + out_html(" COLSPAN="); + sprintf(buf, "%i", currow->at(curfield).colspan); + out_html(buf); + } + if (currow->at(curfield).rowspan>1) { + char buf[5]; + out_html(" ROWSPAN="); + sprintf(buf, "%i", currow->at(curfield).rowspan); + out_html(buf); + } + j=j+currow->at(curfield).colspan; + out_html(">"); + if (currow->at(curfield).size) out_html(change_to_size(currow->at(curfield).size)); + if (currow->at(curfield).font) out_html(set_font(currow->at(curfield).font)); + switch (currow->at(curfield).align) { + case '=': out_html("

"); break; + case '_': out_html("
"); break; + default: + out_html(currow->at(curfield).getContents()); + break; + } + if (currow->at(curfield).space) + for (i=0; iat(curfield).space;i++) out_html(" "); + if (currow->at(curfield).font) out_html(set_font("R")); + if (currow->at(curfield).size) out_html(change_to_size(0)); + if (j>=maxcol && currow->at(curfield).align>'@' && currow->at(curfield).align!='_') + out_html("
"); + out_html(""); + } + curfield++; + } + out_html("
\n"); + currow=currow->next; + } + + clear_table(layout); + + if (box && !border) out_html("
"); + out_html("
\n"); + currow=layout; + while (currow) { + j=0; + out_html("
"); + out_html(""); + if (box==2) out_html(""); + if (center) out_html("
\n"); + else out_html("\n"); + if (!oldfillout) out_html("
");
+    fillout=oldfillout;
+    out_html(change_to_size(oldsize));
+    out_html(set_font(oldfont));
+    return c;
+}
+
+static char *scan_expression( char *c, int *result, const unsigned int numLoop )
+{
+    int value=0,value2,sign=1,opex=0;
+    char oper='c';
+
+    if (*c=='!') {
+	c=scan_expression(c+1, &value);
+	value= (!value);
+    } else if (*c=='n') {
+	c++;
+	value=s_nroff;
+    } else if (*c=='t') {
+	c++;
+	value=1-s_nroff;
+    } else if (*c=='\'' || *c=='"' || *c<' ' || (*c=='\\' && c[1]=='(')) {
+	/* ?string1?string2?
+	** test if string1 equals string2.
+	*/
+	char *st1=NULL, *st2=NULL, *h;
+	char *tcmp=NULL;
+	char sep;
+	sep=*c;
+	if (sep=='\\') {
+	    tcmp=c;
+	    c=c+3;
+	}
+	c++;
+	h=c;
+	while (*c!= sep && (!tcmp || tqstrncmp(c,tcmp,4))) c++;
+	*c='\n';
+	scan_troff(h, 1, &st1);
+	*c=sep;
+	if (tcmp) c=c+3;
+	c++;
+	h=c;
+	while (*c!=sep && (!tcmp || tqstrncmp(c,tcmp,4))) c++;
+	*c='\n';
+	scan_troff(h,1,&st2);
+	*c=sep;
+	if (!st1 && !st2) value=1;
+	else if (!st1 || !st2) value=0;
+	else value=(!qstrcmp(st1, st2));
+	delete [] st1;
+        delete [] st2;
+	if (tcmp) c=c+3;
+	c++;
+    } else {
+        while (*c && ( !isspace(*c) || ( numLoop > 0 ) ) && *c!=')' && opex >= 0) {
+	    opex=0;
+	    switch (*c) {
+	    case '(':
+                c = scan_expression( c + 1, &value2, numLoop + 1 );
+		value2=sign*value2;
+		opex=1;
+		break;
+	    case '.':
+	    case '0': case '1':
+	    case '2': case '3':
+	    case '4': case '5':
+	    case '6': case '7':
+	    case '8': case '9': {
+		int num=0,denum=1;
+		value2=0;
+		while (isdigit(*c)) value2=value2*10+((*c++)-'0');
+		if (*c=='.' && isdigit(c[1])) {
+		    c++;
+		    while (isdigit(*c)) {
+			num=num*10+((*c++)-'0');
+			denum=denum*10;
+		    }
+		}
+		if (isalpha(*c)) {
+		    /* scale indicator */
+		    switch (*c) {
+		    case 'i': /* inch -> 10pt */
+			value2=value2*10+(num*10+denum/2)/denum;
+			num=0;
+			break;
+		    default:
+			break;
+		    }
+		    c++;
+		}
+		value2=value2+(num+denum/2)/denum;
+		value2=sign*value2;
+		opex=1;
+                if (*c=='.')
+                    opex = -1;
+
+	    }
+            break;
+	    case '\\':
+		c=scan_escape(c+1);
+		value2=intresult*sign;
+		if (isalpha(*c)) c++; /* scale indicator */
+		opex=1;
+		break;
+	    case '-':
+		if (oper) { sign=-1; c++; break; }
+	    case '>':
+	    case '<':
+	    case '+':
+	    case '/':
+	    case '*':
+	    case '%':
+	    case '&':
+	    case '=':
+	    case ':':
+		if (c[1]=='=') oper=(*c++) +16; else oper=*c;
+		c++;
+		break;
+	    default: c++; break;
+	    }
+	    if (opex > 0) {
+		sign=1;
+		switch (oper) {
+		case 'c': value=value2; break;
+		case '-': value=value-value2; break;
+		case '+': value=value+value2; break;
+		case '*': value=value*value2; break;
+		case '/': if (value2) value=value/value2; break;
+		case '%': if (value2) value=value%value2; break;
+		case '<': value=(value': value=(value>value2); break;
+		case '>'+16: value=(value>=value2); break;
+		case '<'+16: value=(value<=value2); break;
+		case '=': case '='+16: value=(value==value2); break;
+		case '&': value = (value && value2); break;
+		case ':': value = (value || value2); break;
+		default:
+                    {
+                        kdDebug(7107) << "Unknown operator " << char(oper) << endl;
+                    }
+		}
+		oper=0;
+	    }
+	}
+	if (*c==')') c++;
+    }
+    *result=value;
+    return c;
+}
+
+static char *scan_expression(char *c, int *result)
+{
+    return scan_expression( c, result, 0 );
+}
+
+static void trans_char(char *c, char s, char t)
+{
+    char *sl=c;
+    int slash=0;
+    while (*sl!='\n' || slash) {
+	if (!slash) {
+	    if (*sl==escapesym)
+		slash=1;
+	    else if (*sl==s)
+		*sl=t;
+	} else slash=0;
+	sl++;
+    }
+}
+
+// 2004-10-19, patched by Waldo Bastian :
+// Fix handling of lines like:
+// .TH FIND 1L \" -*- nroff -*-
+// Where \" indicates the start of comment.
+// 
+// The problem is the \" handling in fill_words(), the return value
+// indicates the end of the word as well as the end of the line, which makes it
+// basically impossible to express that the end of the last word is not the end of
+// the line.
+// 
+// I have corrected that by adding an extra parameter 'next_line' that returns a
+// pointer to the next line, while the function itself returns a pointer to the end
+// of the last word.
+static char *fill_words(char *c, char *words[], int *n, bool newline, char **next_line)
+{
+    char *sl=c;
+    int slash=0;
+    int skipspace=0;
+    *n=0;
+    words[*n]=sl;
+    while (*sl && (*sl!='\n' || slash)) {
+	if (!slash) {
+	    if (*sl=='"') {
+                if (skipspace && (*(sl+1)=='"'))
+                    *sl++ = '\a';
+                else {
+        	    *sl='\a';
+	            skipspace=!skipspace;
+                }
+	    } else if (*sl==escapesym) {
+		slash=1;
+                if (sl[1]=='\n')
+                    *sl='\a';
+	    } else if ((*sl==' ' || *sl=='\t') && !skipspace) {
+		if (newline) *sl='\n';
+		if (words[*n]!=sl) (*n)++;
+		words[*n]=sl+1;
+	    }
+	} else {
+	    if (*sl=='"') {
+		sl--;
+		if (newline) *sl='\n';
+		if (words[*n]!=sl) (*n)++;
+		if (next_line)
+		{
+		    char *eow = sl;
+		    sl++;
+		    while (*sl && *sl !='\n') sl++;
+		    *next_line = sl;
+		    return eow;
+		}
+		return sl;
+	    }
+	    slash=0;
+	}
+	sl++;
+    }
+    if (sl!=words[*n]) (*n)++;
+    if (next_line) *next_line = sl+1;
+    return sl;
+}
+
+static const char *abbrev_list[] = {
+    "GSBG", "Getting Started ",
+    "SUBG", "Customizing SunOS",
+    "SHBG", "Basic Troubleshooting",
+    "SVBG", "SunView User's Guide",
+    "MMBG", "Mail and Messages",
+    "DMBG", "Doing More with SunOS",
+    "UNBG", "Using the Network",
+    "GDBG", "Games, Demos & Other Pursuits",
+    "CHANGE", "SunOS 4.1 Release Manual",
+    "INSTALL", "Installing SunOS 4.1",
+    "ADMIN", "System and Network Administration",
+    "SECUR", "Security Features Guide",
+    "PROM", "PROM User's Manual",
+    "DIAG", "Sun System Diagnostics",
+    "SUNDIAG", "Sundiag User's Guide",
+    "MANPAGES", "SunOS Reference Manual",
+    "REFMAN", "SunOS Reference Manual",
+    "SSI", "Sun System Introduction",
+    "SSO", "System Services Overview",
+    "TEXT", "Editing Text Files",
+    "DOCS", "Formatting Documents",
+    "TROFF", "Using nroff and troff",
+    "INDEX", "Global Index",
+    "CPG", "C Programmer's Guide",
+    "CREF", "C Reference Manual",
+    "ASSY", "Assembly Language Reference",
+    "PUL", "Programming Utilities and Libraries",
+    "DEBUG", "Debugging Tools",
+    "NETP", "Network Programming",
+    "DRIVER", "Writing Device Drivers",
+    "STREAMS", "STREAMS Programming",
+    "SBDK", "SBus Developer's Kit",
+    "WDDS", "Writing Device Drivers for the SBus",
+    "FPOINT", "Floating-Point Programmer's Guide",
+    "SVPG", "SunView 1 Programmer's Guide",
+    "SVSPG", "SunView 1 System Programmer's Guide",
+    "PIXRCT", "Pixrect Reference Manual",
+    "CGI", "SunCGI Reference Manual",
+    "CORE", "SunCore Reference Manual",
+    "4ASSY", "Sun-4 Assembly Language Reference",
+    "SARCH", "SPARC Architecture Manual",
+    "KR", "The C Programming Language",
+    NULL, NULL };
+
+static const char *lookup_abbrev(char *c)
+{
+    int i=0;
+
+    if (!c) return "";
+    while (abbrev_list[i] && qstrcmp(c,abbrev_list[i])) i=i+2;
+    if (abbrev_list[i]) return abbrev_list[i+1];
+    else return c;
+}
+
+static const char *section_list[] = {
+#ifdef Q_OS_SOLARIS
+    // for Solaris
+    "1", "User Commands",
+    "1B", "SunOS/BSD Compatibility Package Commands",
+    "1b", "SunOS/BSD Compatibility Package Commands",
+    "1C", "Communication Commands ",
+    "1c", "Communication Commands",
+    "1F", "FMLI Commands ",
+    "1f", "FMLI Commands",
+    "1G", "Graphics and CAD Commands ",
+    "1g", "Graphics and CAD Commands ",
+    "1M", "Maintenance Commands",
+    "1m", "Maintenance Commands",
+    "1S", "SunOS Specific Commands",
+    "1s", "SunOS Specific Commands",
+    "2", "System Calls",
+    "3", "C Library Functions",
+    "3B", "SunOS/BSD Compatibility Library Functions",
+    "3b", "SunOS/BSD Compatibility Library Functions",
+    "3C", "C Library Functions",
+    "3c", "C Library Functions",
+    "3E", "C Library Functions",
+    "3e", "C Library Functions",
+    "3F", "Fortran Library Routines",
+    "3f", "Fortran Library Routines",
+    "3G", "C Library Functions",
+    "3g", "C Library Functions",
+    "3I", "Wide Character Functions",
+    "3i", "Wide Character Functions",
+    "3K", "Kernel VM Library Functions",
+    "3k", "Kernel VM Library Functions",
+    "3L", "Lightweight Processes Library",
+    "3l", "Lightweight Processes Library",
+    "3M", "Mathematical Library",
+    "3m", "Mathematical Library",
+    "3N", "Network Functions",
+    "3n", "Network Functions",
+    "3R", "Realtime Library",
+    "3r", "Realtime Library",
+    "3S", "Standard I/O Functions",
+    "3s", "Standard I/O Functions",
+    "3T", "Threads Library",
+    "3t", "Threads Library",
+    "3W", "C Library Functions",
+    "3w", "C Library Functions",
+    "3X", "Miscellaneous Library Functions",
+    "3x", "Miscellaneous Library Functions",
+    "4", "File Formats",
+    "4B", "SunOS/BSD Compatibility Package File Formats",
+    "4b", "SunOS/BSD Compatibility Package File Formats",
+    "5", "Headers, Tables, and Macros",
+    "6", "Games and Demos",
+    "7", "Special Files",
+    "7B", "SunOS/BSD Compatibility Special Files",
+    "7b", "SunOS/BSD Compatibility Special Files",
+    "8", "Maintenance Procedures",
+    "8C", "Maintenance Procedures",
+    "8c", "Maintenance Procedures",
+    "8S", "Maintenance Procedures",
+    "8s", "Maintenance Procedures",
+    "9", "DDI and DKI",
+    "9E", "DDI and DKI Driver Entry Points",
+    "9e", "DDI and DKI Driver Entry Points",
+    "9F", "DDI and DKI Kernel Functions",
+    "9f", "DDI and DKI Kernel Functions",
+    "9S", "DDI and DKI Data Structures",
+    "9s", "DDI and DKI Data Structures",
+    "L", "Local Commands",
+#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+    "1", "General Commands",
+    "2", "System Calls",
+    "3", "Library Functions",
+    "4", "Kernel Interfaces",
+    "5", "File Formats",
+    "6", "Games",
+    "7", "Miscellaneous Information",
+    "8", "System Manager's Manuals",
+    "9", "Kernel Developer's Manuals",
+#else
+    // Other OS
+    "1", "User Commands ",
+    "1C", "User Commands",
+    "1G", "User Commands",
+    "1S", "User Commands",
+    "1V", "User Commands ",
+    "2", "System Calls",
+    "2V", "System Calls",
+    "3", "C Library Functions",
+    "3C", "Compatibility Functions",
+    "3F", "Fortran Library Routines",
+    "3K", "Kernel VM Library Functions",
+    "3L", "Lightweight Processes Library",
+    "3M", "Mathematical Library",
+    "3N", "Network Functions",
+    "3R", "RPC Services Library",
+    "3S", "Standard I/O Functions",
+    "3V", "C Library Functions",
+    "3X", "Miscellaneous Library Functions",
+    "4", "Devices and Network Interfaces",
+    "4F", "Protocol Families",
+    "4I", "Devices and Network Interfaces",
+    "4M", "Devices and Network Interfaces",
+    "4N", "Devices and Network Interfaces",
+    "4P", "Protocols",
+    "4S", "Devices and Network Interfaces",
+    "4V", "Devices and Network Interfaces",
+    "5", "File Formats",
+    "5V", "File Formats",
+    "6", "Games and Demos",
+    "7", "Environments, Tables, and Troff Macros",
+    "7V", "Environments, Tables, and Troff Macros",
+    "8", "Maintenance Commands",
+    "8C", "Maintenance Commands",
+    "8S", "Maintenance Commands",
+    "8V", "Maintenance Commands",
+    "L", "Local Commands",
+#endif
+    // The defaults
+    NULL, "Misc. Reference Manual Pages",
+    NULL, NULL
+};
+
+static const char *section_name(char *c)
+{
+    int i=0;
+
+    if (!c) return "";
+    while (section_list[i] && qstrcmp(c,section_list[i])) i=i+2;
+    if (section_list[i+1]) return section_list[i+1];
+    else return c;
+}
+
+static char *skip_till_newline(char *c)
+{
+    int lvl=0;
+
+    while (*c && (*c!='\n' || lvl>0)) {
+	if (*c=='\\') {
+	    c++;
+	    if (*c=='}') lvl--; else if (*c=='{') lvl++;
+	}
+	c++;
+    }
+    if (*c) c++;
+    if (lvl<0 && newline_for_fun) {
+	newline_for_fun = newline_for_fun+lvl;
+	if (newline_for_fun<0) newline_for_fun=0;
+    }
+    return c;
+}
+
+static bool s_whileloop = false;
+
+/// Processing the .while request
+static void request_while( char*& c, int j, bool mdoc )
+{
+    // ### TODO: .break and .continue
+    kdDebug(7107) << "Entering .while" << endl;
+    c += j;
+    char* newline = skip_till_newline( c );
+    const char oldchar = *newline;
+    *newline = 0;
+    // We store the full .while stuff into a TQCString as if it would be a macro
+    const TQCString macro = c ;
+    kdDebug(7107) << "'Macro' of .while" << endl << macro << endl;
+    // Prepare for continuing after .while loop end
+    *newline = oldchar;
+    c = newline;
+    // Process -while loop
+    const bool oldwhileloop = s_whileloop;
+    s_whileloop = true;
+    int result = true; // It must be an int due to the call to scan_expression
+    while ( result )
+    {
+        // Unlike for a normal macro, we have the condition at start, so we do not need to prepend extra bytes
+        char* liveloop = tqstrdup( macro.data() );
+        kdDebug(7107) << "Scanning .while condition" << endl;
+        kdDebug(7101) << "Loop macro " << liveloop << endl;
+        char* end_expression = scan_expression( liveloop, &result );
+        kdDebug(7101) << "After " << end_expression << endl;
+        if ( result )
+        {
+            kdDebug(7107) << "New .while iteration" << endl;
+            // The condition is true, so call the .while's content
+            char* help = end_expression + 1;
+            while ( *help && ( *help == ' '  || *help == '\t' ) )
+                ++help;
+            if ( ! *help )
+            {
+                // We have a problem, so stop .while
+                result = false;
+                break;
+            }
+            if ( mdoc )
+                scan_troff_mandoc( help, false, 0 );
+            else
+                scan_troff( help, false, 0 );
+        }
+        delete[] liveloop;
+    }
+
+    //
+    s_whileloop = oldwhileloop;
+    kdDebug(7107) << "Ending .while" << endl;
+}
+
+const int max_wordlist = 100;
+
+/// Processing mixed fonts reqiests like .BI
+static void request_mixed_fonts( char*& c, int j, const char* font1, const char* font2, const bool mode, const bool inFMode )
+{
+    c += j;
+    if (*c=='\n') c++;
+    int words;
+    char *wordlist[max_wordlist];
+    fill_words(c, wordlist, &words, true, &c);
+    for (int i=0; i s_ifelseval;
+
+// Process a (mdoc) request involving quotes
+static char* process_quote(char* c, int j, const char* open, const char* close)
+{
+    trans_char(c,'"','\a');
+    c+=j;
+    if (*c=='\n') c++; // ### TODO: why? Quote requests cannot be empty!
+    out_html(open);
+    c=scan_troff_mandoc(c,1,0);
+    out_html(close);
+    out_html(NEWLINE);
+    if (fillout)
+      curpos++;
+    else
+      curpos=0;
+    return c;
+}
+
+/**
+ * Is the char \p ch a puntuaction in sence of mdoc(7)
+ */
+static bool is_mdoc_punctuation( const char ch )
+{
+    if ( ( ch >= '0' &&  ch <= '9' ) || ( ch >='A' && ch <='Z' ) || ( ch >= 'a' && ch <= 'z' ) )
+        return false;
+    else if ( ch == '.' || ch == ',' || ch == ';' || ch == ':' || ch == '(' || ch == ')'
+        || ch == '[' || ch == ']' )
+        return true;
+    else
+        return false;
+}
+
+/**
+ * Can the char \p c be part of an identifier
+ * \note For groff, an identifier can consist of nearly all ASCII printable non-white-space characters
+ * See info:/groff/Identifiers
+ */
+static bool is_identifier_char( const char c )
+{
+    if ( c >= '!' && c <= '[' ) // Include digits and upper case
+        return true;
+    else if ( c >= ']' && c <= '~' ) // Include lower case
+        return true;
+    else if ( c== '\\' )
+        return false; // ### TODO: it should be treated as escape instead!
+    return false;
+}
+
+static TQCString scan_identifier( char*& c )
+{
+    char* h = c; // help pointer
+    // ### TODO Groff seems to eat nearly everything as identifier name (info:/groff/Identifiers)
+    while ( *h && *h != '\a' && *h != '\n' && is_identifier_char( *h ) )
+        ++h;
+    const char tempchar = *h;
+    *h = 0;
+    const TQCString name = c;
+    *h = tempchar;
+    if ( name.isEmpty() )
+    {
+        kdDebug(7107) << "EXCEPTION: identifier empty!" << endl;
+    }
+    c = h;
+    return name;
+}
+
+static char *scan_request(char *c)
+{
+    // mdoc(7) stuff
+    static bool mandoc_synopsis=false; /* True if we are in the synopsis section */
+    static bool mandoc_command=false;  /* True if this is mdoc(7) page */
+    static int mandoc_bd_options; /* Only copes with non-nested Bd's */
+    static int function_argument=0; // Number of function argument (.Fo, .Fa, .Fc)
+    // man(7) stuff
+    static bool ur_ignore=false; // Has .UR a parameter : (for .UE to know if or not to write )
+
+    int i=0;
+    bool mode=false;
+    char *h=0;
+    char *wordlist[max_wordlist];
+    int words;
+    char *sl;
+    while (*c==' ' || *c=='\t') c++; // Spaces or tabs allowed between control character and request
+    if (c[0]=='\n') return c+1;
+    if (c[0]==escapesym)
+    {
+        /* some pages use .\" .\$1 .\} */
+	/* .\$1 is too difficult/stuppid */
+        if (c[1]=='$')
+        {
+            kdDebug(7107) << "Found .\\$" << endl;
+            c=skip_till_newline(c); // ### TODO
+        }
+	else
+
+	    c = scan_escape(c+1);
+    }
+    else
+    {
+        int nlen = 0;
+        TQCString macroName;
+        while (c[nlen] && (c[nlen] != ' ') && (c[nlen] != '\t') && (c[nlen] != '\n') && (c[nlen] != escapesym))
+        {
+            macroName+=c[nlen];
+            nlen++;
+        }
+        int j = nlen;
+        while (c[j] && c[j]==' ' || c[j]=='\t') j++;
+        /* search macro database of self-defined macros */
+        TQMap::iterator it=s_stringDefinitionMap.find(macroName);
+        if (it!=s_stringDefinitionMap.end())
+        {
+            kdDebug(7107) << "CALLING MACRO: " << macroName << endl;
+            const TQCString oldDollarZero = s_dollarZero; // Previous value of $0
+            s_dollarZero = macroName;
+            sl=fill_words(c+j, wordlist, &words, true, &c);
+            *sl='\0';
+            for (i=1;i oldArgumentList( s_argumentList );
+                s_argumentList.clear();
+                for ( i = 0 ; i < max_wordlist; i++ )
+                {
+                    if (!wordlist[i])
+                        break;
+                    s_argumentList.push_back( wordlist[i] );
+                }
+                const int onff=newline_for_fun;
+                if (mandoc_command)
+                    scan_troff_mandoc( work + 1, 0, NULL );
+                else
+                    scan_troff( work + 1, 0, NULL);
+                delete[] work;
+                newline_for_fun=onff;
+                s_argumentList = oldArgumentList;
+            }
+            for (i=0; i::iterator it=s_stringDefinitionMap.find(name);
+                    if (it==s_stringDefinitionMap.end())
+                    {
+                        StringDefinition def;
+                        def.m_length=0;
+                        def.m_output=result;
+                        s_stringDefinitionMap.insert(name,def);
+                    }
+                    else
+                    {
+                        (*it).m_length=0;
+                        (*it).m_output=result;
+                    }
+                    delete[] result;
+                    if (*c) *c='.';
+                    c=skip_till_newline(c);
+                    kdDebug(7107) << "end .di" << endl;
+                    break;
+                }
+                case REQ_ds: // groff(7) "Define String variable"
+                    mode=true;
+                case REQ_as: // groff (7) "Append String variable"
+                {
+                    kdDebug(7107) << "start .ds/.as" << endl;
+                    int oldcurpos=curpos;
+                    c+=j;
+                    const TQCString name( scan_identifier( c) );
+                    if ( name.isEmpty() )
+                        break;
+                    while (*c && isspace(*c)) c++;
+                    if (*c && *c=='"') c++;
+                    single_escape=true;
+                    curpos=0;
+                    char* result=0;
+                    c=scan_troff(c,1,&result);
+                    TQMap::iterator it=s_stringDefinitionMap.find(name);
+                    if (it==s_stringDefinitionMap.end())
+                    {
+                        StringDefinition def;
+                        def.m_length=curpos;
+                        def.m_output=result;
+                        s_stringDefinitionMap.insert(name,def);
+                    }
+                    else
+                    {
+                        if (mode)
+                        {   // .ds Defining String
+                            (*it).m_length=curpos;
+                            (*it).m_output=result;
+                        }
+                        else
+                        {   // .as Appending String
+                            (*it).m_length+=curpos;
+                            (*it).m_output+=result;
+                        }
+                    }
+                    delete[] result;
+                    single_escape=false;
+                    curpos=oldcurpos;
+                    kdDebug(7107) << "end .ds/.as" << endl;
+                    break;
+                }
+                case REQ_br: // groff(7) "line BReak"
+                {
+                    if (still_dd) 
+                        out_html("
"); // ### VERIFY (does not look like generating good HTML) + else + out_html("
\n"); + curpos=0; + c=c+j; + if (c[0]==escapesym) c=scan_escape(c+1); + c=skip_till_newline(c); + break; + } + case REQ_c2: // groff(7) "reset non-break Control character" (2 means non-break) + { + c=c+j; + if (*c!='\n') + nobreaksym=*c; + else + nobreaksym='\''; + c=skip_till_newline(c); + break; + } + case REQ_cc: // groff(7) "reset Control Character" + { + c=c+j; + if (*c!='\n') + controlsym=*c; + else + controlsym='.'; + c=skip_till_newline(c); + break; + } + case REQ_ce: // groff (7) "CEnter" + { + c=c+j; + if (*c=='\n') + i=1; + else + { + i=0; + while ('0'<=*c && *c<='9') + { + i=i*10+*c-'0'; + c++; + } + } + c=skip_till_newline(c); + /* center next i lines */ + if (i>0) + { + out_html("
\n"); + while (i && *c) + { + char *line=NULL; + c=scan_troff(c,1, &line); + if (line && tqstrncmp(line, "
", 4)) + { + out_html(line); + out_html("
\n"); + delete [] line; // ### FIXME: memory leak! + i--; + } + } + out_html("
\n"); + curpos=0; + } + break; + } + case REQ_ec: // groff(7) "reset Escape Character" + { + c=c+j; + if (*c!='\n') + escapesym=*c; + else + escapesym='\\'; + break; + c=skip_till_newline(c); + } + case REQ_eo: // groff(7) "turn Escape character Off" + { + escapesym='\0'; + c=skip_till_newline(c); + break; + } + case REQ_ex: // groff(7) "EXit" + { + return 0; + break; + } + case REQ_fc: // groff(7) "set Field and pad Character" + { + c=c+j; + if (*c=='\n') + fieldsym=padsym='\0'; + else + { + fieldsym=c[0]; + padsym=c[1]; + } + c=skip_till_newline(c); + break; + } + case REQ_fi: // groff(7) "FIll" + { + if (!fillout) + { + out_html(set_font("R")); + out_html(change_to_size('0')); + out_html("
\n"); + } + curpos=0; + fillout=1; + c=skip_till_newline(c); + break; + } + case REQ_ft: // groff(7) "FonT" + { + c += j; + h = skip_till_newline( c ); + const char oldChar = *h; + *h = 0; + const TQCString name = c; + // ### TODO: name might contain a variable + if ( name.isEmpty() ) + out_html( set_font( "P" ) ); // Previous font + else + out_html( set_font( name ) ); + *h = oldChar; + c = h; + break; + } + case REQ_el: // groff(7) "ELse" + { + int ifelseval = s_ifelseval.pop(); + /* .el anything : else part of if else */ + if (ifelseval) + { + c=c+j; + c[-1]='\n'; + c=scan_troff(c,1,NULL); + } + else + c=skip_till_newline(c+j); + break; + } + case REQ_ie: // groff(7) "If with Else" + /* .ie c anything : then part of if else */ + case REQ_if: // groff(7) "IF" + { + /* .if c anything + * .if !c anything + * .if N anything + * .if !N anything + * .if 'string1'string2' anything + * .if !'string1'string2' anything + */ + c=c+j; + c=scan_expression(c, &i); + if (request == REQ_ie) + { + int ifelseval=!i; + s_ifelseval.push( ifelseval ); + } + if (i) + { + *c='\n'; + c++; + c=scan_troff(c,1,NULL); + } + else + c=skip_till_newline(c); + break; + } + case REQ_ig: // groff(7) "IGnore" + { + const char *endwith="..\n"; + i=3; + c=c+j; + if (*c!='\n' && *c != '\\') + { + /* Not newline or comment */ + endwith=c-1;i=1; + c[-1]='.'; + while (*c && *c!='\n') c++,i++; + } + c++; + while (*c && tqstrncmp(c,endwith,i)) while (*c++!='\n'); + while (*c && *c++!='\n'); + break; + } + case REQ_nf: // groff(7) "No Filling" + { + if (fillout) + { + out_html(set_font("R")); + out_html(change_to_size('0')); + out_html("
\n");
+                    }
+                    curpos=0;
+                    fillout=0;
+                    c=skip_till_newline(c);
+                    break;
+                }
+                case REQ_ps: // groff(7) "previous Point Size"
+                {
+                    c=c+j;
+                    if (*c=='\n')
+                        out_html(change_to_size('0'));
+                    else
+                    {
+                        j=0; i=0;
+                        if (*c=='-')
+                        {
+                            j= -1;
+                            c++;
+                        }
+                        else if (*c=='+')
+                            j=1;c++;
+                        c=scan_expression(c, &i);
+                        if (!j)
+                        {
+                            j=1;
+                            if (i>5) i=i-10;
+                        }
+                        out_html(change_to_size(i*j));
+                    }
+                    c=skip_till_newline(c);
+                    break;
+                }
+                case REQ_sp: // groff(7) "SKip one line"
+                {
+                    c=c+j;
+                    if (fillout)
+                        out_html("

"); + else + { + out_html(NEWLINE); + } + curpos=0; + c=skip_till_newline(c); + break; + } + case REQ_so: // groff(7) "Include SOurce file" + { + char *buf; + char *name=NULL; + curpos=0; + c=c+j; + if (*c=='/') + h=c; + else + { + h=c-3; + h[0]='.'; + h[1]='.'; + h[2]='/'; + } + while (*c!='\n') c++; + *c='\0'; + scan_troff(h,1, &name); + if (name[3]=='/') + h=name+3; + else + h=name; + /* this works alright, except for section 3 */ + buf=read_man_page(h); + if (!buf) + { + kdDebug(7107) << "Unable to open or read file: .so " << (h) << endl; + out_html("
" + "man2html: unable to open or read file.\n"); + out_html(h); + out_html("
\n"); + } + else + scan_troff(buf+1,0,NULL); + delete [] buf; + delete [] name; + + *c++='\n'; + break; + } + case REQ_ta: // gorff(7) "set TAbulators" + { + c=c+j; + j=0; + while (*c!='\n') + { + sl=scan_expression(c, &tabstops[j]); + if (j>0 && (*c=='-' || *c=='+')) tabstops[j]+=tabstops[j-1]; + c=sl; + while (*c==' ' || *c=='\t') c++; + j++; + } + maxtstop=j; + curpos=0; + break; + } + case REQ_ti: // groff(7) "Temporary Indent" + { + /*while (itemdepth || dl_set[itemdepth]) { + out_html("\n"); + if (dl_set[itemdepth]) dl_set[itemdepth]=0; + else itemdepth--; + }*/ + out_html("
\n"); + c=c+j; + c=scan_expression(c, &j); + for (i=0; i"); + }; + out_html(set_font("R")); + out_html(NEWLINE); + if (!fillout) + curpos=0; + else + curpos++; + break; + } + case REQ_Fn: // mdoc(7) for "Function calls" + { + // brackets and commas have to be inserted automatically + c+=j; + if (*c=='\n') c++; + sl=fill_words(c, wordlist, &words, true, &c); + if ( words ) + { + for (i=0; i"); + out_html(NEWLINE); + if (!fillout) + curpos=0; + else + curpos++; + break; + } + case REQ_Fo: // mdoc(7) "Function definition Opening" + { + char* font[2] = { "B", "R" }; + c+=j; + if (*c=='\n') c++; + char *eol=strchr(c,'\n'); + char *semicolon=strchr(c,';'); + if ((semicolon!=0) && (semicolon 0 + out_html(set_font("R")); + out_html(NEWLINE); + if (!fillout) + curpos=0; + else + curpos++; + break; + } + case REQ_Fc:// mdoc(7) "Function definition Close" + { + // .Fc has no parameter + c+=j; + c=skip_till_newline(c); + char* font[2] = { "B", "R" }; + out_html(set_font(font[i&1])); + out_html(")"); + out_html(set_font("R")); + if (mandoc_synopsis) + out_html("
"); + out_html(NEWLINE); + if (!fillout) + curpos=0; + else + curpos++; + function_argument=0; // Reset the count variable + break; + } + case REQ_Fa: // mdoc(7) "Function definition argument" + { + char* font[2] = { "B", "R" }; + c+=j; + if (*c=='\n') c++; + sl=fill_words(c, wordlist, &words, true, &c); + out_html(set_font(font[i&1])); + // function_argument==0 means that we had no .Fo before, e.g. in mdoc.samples(7) + if (function_argument > 1) + { + out_html(", "); + curpos+=2; + function_argument++; + } + else if (function_argument==1) + { + // We are only at the first parameter + function_argument++; + } + for (i=0; ia b ] */ + mode=true; + out_html(set_font("R")); + out_html("["); + curpos++; + request_mixed_fonts( c, j, "B", "I", true, false ); + break; + // Do not break! + } + case REQ_Ft: //perhaps "Function return type" + { + request_mixed_fonts( c, j, "B", "I", false, true ); + break; + } + case REQ_BR: + { + request_mixed_fonts( c, j, "B", "R", false, false ); + break; + } + case REQ_BI: + { + request_mixed_fonts( c, j, "B", "I", false, false ); + break; + } + case REQ_IB: + { + request_mixed_fonts( c, j, "I", "B", false, false ); + break; + } + case REQ_IR: + { + request_mixed_fonts( c, j, "I", "R", false, false ); + break; + } + case REQ_RB: + { + request_mixed_fonts( c, j, "R", "B", false, false ); + break; + } + case REQ_RI: + { + request_mixed_fonts( c, j, "R", "I", false, false ); + break; + } + case REQ_DT: // man(7) "Default Tabulators" + { + for (j=0;j<20; j++) tabstops[j]=(j+1)*8; + maxtstop=20; + c=skip_till_newline(c); + break; + } + case REQ_IP: // man(7) "Ident Paragraph" + { + sl=fill_words(c+j, wordlist, &words, true, &c); + if (!dl_set[itemdepth]) + { + out_html("
\n"); + dl_set[itemdepth]=1; + } + out_html("
"); + if (words) + scan_troff(wordlist[0], 1,NULL); + out_html("
"); + curpos=0; + break; + } + case REQ_TP: // man(7) "hanging Tag Paragraph" + { + if (!dl_set[itemdepth]) + { + out_html("

\n"); + dl_set[itemdepth]=1; + } + out_html("
"); + c=skip_till_newline(c); + /* somewhere a definition ends with '.TP' */ + if (!*c) + still_dd=true; + else + { + // HACK for proc(5) + while (c[0]=='.' && c[1]=='\\' && c[2]=='\"') + { + // We have a comment, so skip the line + c=skip_till_newline(c); + } + c=scan_troff(c,1,NULL); + out_html("
"); + } + curpos=0; + break; + } + case REQ_IX: // "INdex" ### TODO: where is it defined? + { + /* general index */ + c=skip_till_newline(c); + break; + } + case REQ_P: // man(7) "Paragraph" + case REQ_LP:// man(7) "Paragraph" + case REQ_PP:// man(7) "Paragraph; reset Prevailing indent" + { + if (dl_set[itemdepth]) + { + out_html("
\n"); + dl_set[itemdepth]=0; + } + if (fillout) + out_html("

\n"); + else + { + out_html(NEWLINE); + } + curpos=0; + c=skip_till_newline(c); + break; + } + case REQ_HP: // man(7) "Hanging indent Paragraph" + { + if (!dl_set[itemdepth]) + { + out_html("
"); + dl_set[itemdepth]=1; + } + out_html("
\n"); + still_dd=true; + c=skip_till_newline(c); + curpos=0; + break; + } + case REQ_PD: // man(7) "Paragraph Distance" + { + c=skip_till_newline(c); + break; + } + case REQ_Rs: // mdoc(7) "Relative margin Start" + case REQ_RS: // man(7) "Relative margin Start" + { + sl=fill_words(c+j, wordlist, &words, true, 0); + j=1; + if (words>0) scan_expression(wordlist[0], &j); + if (j>=0) + { + itemdepth++; + dl_set[itemdepth]=0; + out_html("
"); + c=skip_till_newline(c); + curpos=0; + break; + } + } + case REQ_Re: // mdoc(7) "Relative margin End" + case REQ_RE: // man(7) "Relative margin End" + { + if (itemdepth > 0) + { + if (dl_set[itemdepth]) out_html("
"); + out_html("
\n"); + itemdepth--; + } + c=skip_till_newline(c); + curpos=0; + break; + } + case REQ_SB: // man(7) "Small; Bold" + { + out_html(set_font("B")); + out_html(""); + trans_char(c,'"','\a'); // ### VERIFY + c=scan_troff(c+j, 1, NULL); + out_html(""); + out_html(set_font("R")); + break; + } + case REQ_SM: // man(7) "SMall" + { + c=c+j; + if (*c=='\n') c++; + out_html(""); + trans_char(c,'"','\a'); // ### VERIFY + c=scan_troff(c,1,NULL); + out_html(""); + break; + } + case REQ_Ss: // mdoc(7) "Sub Section" + mandoc_command = 1; + case REQ_SS: // mdoc(7) "Sub Section" + mode=true; + case REQ_Sh: // mdoc(7) "Sub Header" + /* hack for fallthru from above */ + mandoc_command = !mode || mandoc_command; + case REQ_SH: // man(7) "Sub Header" + { + c=c+j; + if (*c=='\n') c++; + while (itemdepth || dl_set[itemdepth]) + { + out_html("
\n"); + if (dl_set[itemdepth]) + dl_set[itemdepth]=0; + else if (itemdepth > 0) + itemdepth--; + } + out_html(set_font("R")); + out_html(change_to_size(0)); + if (!fillout) + { + fillout=1; + out_html("
"); + } + trans_char(c,'"', '\a'); + if (section) + { + out_html("\n"); + section=0; + } + if (mode) + out_html("\n

"); + else + out_html("\n

"); + mandoc_synopsis = tqstrncmp(c, "SYNOPSIS", 8) == 0; + c = mandoc_command ? scan_troff_mandoc(c,1,NULL) : scan_troff(c,1,NULL); + if (mode) + out_html("

\n"); + else + out_html("\n"); + out_html("
\n"); + + section=1; + curpos=0; + break; + } + case REQ_Sx: // mdoc(7) + { + // reference to a section header + out_html(set_font("B")); + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + c=scan_troff(c, 1, NULL); + out_html(set_font("R")); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_TS: // ### TODO where is it defined? (tbl?) + { + c=scan_table(c); + break; + } + case REQ_Dt: /* mdoc(7) */ + mandoc_command = true; + case REQ_TH: // man(7) "Title Header" + { + if (!output_possible) + { + sl = fill_words(c+j, wordlist, &words, true, &c); + // ### TODO: the page should be displayed even if it is "anonymous" (words==0) + if (words>=1) + { + for (i=1; i\n\n"); +#ifdef SIMPLE_MAN2HTML + // Most English man pages are in ISO-8859-1 + out_html("\n"); +#else + // kio_man transforms from local to UTF-8 + out_html("\n"); +#endif + out_html(""); + out_html(scan_troff(wordlist[0], 0, NULL)); + out_html( " Manpage\n"); + out_html( "\n" ); + out_html( "\n"); + out_html( "\n\n" ); + out_html("\n\n" ); + out_html("
\n" ); + out_html("
\n"); + out_html("\"Top\n"); + out_html("
\n"); + + out_html("
\n"); + out_html("\"Top\n"); + out_html("
\n"); + out_html("
\n"); + out_html( scan_troff(wordlist[0], 0, NULL ) ); + out_html("
\n"); + out_html("
\n"); + out_html("
\n"); + out_html("

" ); + out_html( scan_troff(wordlist[0], 0, NULL ) ); + out_html( "

\n" ); + if (words>1) + { + out_html("Section: " ); + if (!mandoc_command && words>4) + out_html(scan_troff(wordlist[4], 0, NULL) ); + else + out_html(section_name(wordlist[1])); + out_html(" ("); + out_html(scan_troff(wordlist[1], 0, NULL)); + out_html(")\n"); + } + else + { + out_html("Section not specified"); + } + *sl='\n'; + } + } + else + { + kdWarning(7107) << ".TH found but output not possible" << endl; + c=skip_till_newline(c); + } + curpos=0; + break; + } + case REQ_TX: // mdoc(7) + { + sl=fill_words(c+j, wordlist, &words, true, &c); + *sl='\0'; + out_html(set_font("I")); + if (words>1) wordlist[1][-1]='\0'; + const char *c2=lookup_abbrev(wordlist[0]); + curpos+=tqstrlen(c2); + out_html(c2); + out_html(set_font("R")); + if (words>1) + out_html(wordlist[1]); + *sl='\n'; + break; + } + case REQ_rm: // groff(7) "ReMove" + /* .rm xx : Remove request, macro or string */ + mode=true; + case REQ_rn: // groff(7) "ReName" + /* .rn xx yy : Rename request, macro or string xx to yy */ + { + kdDebug(7107) << "start .rm/.rn" << endl; + c+=j; + const TQCString name( scan_identifier( c ) ); + if ( name.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty origin string to remove/rename: " << endl; + break; + } + TQCString name2; + if ( !mode ) + { + while (*c && isspace(*c) && *c!='\n') ++c; + name2 = scan_identifier( c ); + if ( name2.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty destination string to rename: " << endl; + break; + } + } + c=skip_till_newline(c); + TQMap::iterator it=s_stringDefinitionMap.find(name); + if (it==s_stringDefinitionMap.end()) + { + kdDebug(7107) << "EXCEPTION: cannot find string to rename or remove: " << name << endl; + } + else + { + if (mode) + { + // .rm ReMove + s_stringDefinitionMap.remove(name); // ### QT4: removeAll + } + else + { + // .rn ReName + StringDefinition def=(*it); + s_stringDefinitionMap.remove(name); // ### QT4: removeAll + s_stringDefinitionMap.insert(name2,def); + } + } + kdDebug(7107) << "end .rm/.rn" << endl; + break; + } + case REQ_nx: // ### TODO in man(7) it is "No filling", not "next file" + /* .nx filename : next file. */ + case REQ_in: // groff(7) "INdent" + { + /* .in +-N : Indent */ + c=skip_till_newline(c); + break; + } + case REQ_nr: // groff(7) "Number Register" + { + kdDebug(7107) << "start .nr" << endl; + c += j; + const TQCString name( scan_identifier( c ) ); + if ( name.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty name for register variable" << endl; + break; + } + while ( *c && ( *c==' ' || *c=='\t' ) ) c++; + int sign = 0; + if ( *c && ( *c == '+' || *c == '-' ) ) + { + if ( *c == '+' ) + sign = 1; + else if ( *c == '-' ) + sign = -1; + } + int value = 0; + int increment = 0; + c=scan_expression( c, &value ); + if ( *c && *c!='\n') + { + while ( *c && ( *c==' ' || *c=='\t' ) ) c++; + c=scan_expression( c, &increment ); + } + c = skip_till_newline( c ); + TQMap ::iterator it = s_numberDefinitionMap.find( name ); + if ( it == s_numberDefinitionMap.end() ) + { + if ( sign < 1 ) + value = -value; + NumberDefinition def( value, increment ); + s_numberDefinitionMap.insert( name, def ); + } + else + { + if ( sign > 0 ) + (*it).m_value += value; + else if ( sign < 0 ) + (*it).m_value += - value; + else + (*it).m_value = value; + (*it).m_increment = increment; + } + kdDebug(7107) << "end .nr" << endl; + break; + } + case REQ_am: // groff(7) "Append Macro" + /* .am xx yy : append to a macro. */ + /* define or handle as .ig yy */ + mode=true; + case REQ_de: // groff(7) "DEfine macro" + /* .de xx yy : define or redefine macro xx; end at .yy (..) */ + /* define or handle as .ig yy */ + { + kdDebug(7107) << "Start .am/.de" << endl; + c+=j; + char *next_line; + sl = fill_words(c, wordlist, &words, true, &next_line); + char *nameStart = wordlist[0]; + c = nameStart; + while (*c && (*c != ' ') && (*c != '\n')) c++; + *c = '\0'; + const TQCString name(nameStart); + + TQCString endmacro; + if (words == 1) + { + endmacro=".."; + } + else + { + endmacro='.'; + c = wordlist[1]; + while (*c && (*c != ' ') && (*c != '\n')) + endmacro+=*c++; + } + c = next_line; + sl=c; + const int length=tqstrlen(endmacro); + while (*c && tqstrncmp(c,endmacro,length)) + c=skip_till_newline(c); + + TQCString macro; + while (sl!=c) + { + if (sl[0]=='\\' && sl[1]=='\\') + { + macro+='\\'; + sl++; + } + else + macro+=*sl; + sl++; + } + + TQMap::iterator it=s_stringDefinitionMap.find(name); + if (it==s_stringDefinitionMap.end()) + { + StringDefinition def; + def.m_length=0; + def.m_output=macro; + s_stringDefinitionMap.insert(name,def); + } + else if (mode) + { + // .am Append Macro + (*it).m_length=0; // It could be formerly a string +// if ((*it).m_output.right(1)!='\n') + if (*((*it).m_output.right(1).data())!='\n') + (*it).m_output+='\n'; + (*it).m_output+=macro; + } + else + { + // .de DEfine macro + (*it).m_length=0; // It could be formerly a string + (*it).m_output=macro; + } + c=skip_till_newline(c); + kdDebug(7107) << "End .am/.de" << endl; + break; + } + case REQ_Bl: // mdoc(7) "Begin List" + { + char list_options[NULL_TERMINATED(MED_STR_MAX)]; + char *nl = strchr(c,'\n'); + c=c+j; + if (dl_set[itemdepth]) + /* These things can nest. */ + itemdepth++; + if (nl) + { + /* Parse list options */ + strlimitcpy(list_options, c, nl - c, MED_STR_MAX); + } + if (strstr(list_options, "-bullet")) + { + /* HTML Unnumbered List */ + dl_set[itemdepth] = BL_BULLET_LIST; + out_html("
    \n"); + } + else if (strstr(list_options, "-enum")) + { + /* HTML Ordered List */ + dl_set[itemdepth] = BL_ENUM_LIST; + out_html("
      \n"); + } + else + { + /* HTML Descriptive List */ + dl_set[itemdepth] = BL_DESC_LIST; + out_html("
      \n"); + } + if (fillout) + out_html("

      \n"); + else + { + out_html(NEWLINE); + } + curpos=0; + c=skip_till_newline(c); + break; + } + case REQ_El: // mdoc(7) "End List" + { + c=c+j; + if (dl_set[itemdepth] & BL_DESC_LIST) + out_html("
      \n"); + else if (dl_set[itemdepth] & BL_BULLET_LIST) + out_html("
\n"); + else if (dl_set[itemdepth] & BL_ENUM_LIST) + out_html("\n"); + dl_set[itemdepth]=0; + if (itemdepth > 0) itemdepth--; + if (fillout) + out_html("

\n"); + else + { + out_html(NEWLINE); + } + curpos=0; + c=skip_till_newline(c); + break; + } + case REQ_It: // mdoc(7) "list ITem" + { + c=c+j; + if (tqstrncmp(c, "Xo", 2) == 0 && isspace(*(c+2))) + c = skip_till_newline(c); + if (dl_set[itemdepth] & BL_DESC_LIST) + { + out_html("
"); + out_html(set_font("B")); + if (*c=='\n') + { + /* Don't allow embedded comms after a newline */ + c++; + c=scan_troff(c,1,NULL); + } + else + { + /* Do allow embedded comms on the same line. */ + c=scan_troff_mandoc(c,1,NULL); + } + out_html(set_font("R")); + out_html(NEWLINE); + out_html("
"); + } + else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) + { + out_html("
  • "); + c=scan_troff_mandoc(c,1,NULL); + out_html(NEWLINE); + } + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Bk: /* mdoc(7) */ + case REQ_Ek: /* mdoc(7) */ + case REQ_Dd: /* mdoc(7) */ + case REQ_Os: // mdoc(7) "Operating System" + { + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + c=scan_troff_mandoc(c, 1, NULL); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Bt: // mdoc(7) "Beta Test" + { + trans_char(c,'"','\a'); + c=c+j; + out_html(" is currently in beta test."); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_At: /* mdoc(7) */ + case REQ_Fx: /* mdoc(7) */ + case REQ_Nx: /* mdoc(7) */ + case REQ_Ox: /* mdoc(7) */ + case REQ_Bx: /* mdoc(7) */ + case REQ_Ux: /* mdoc(7) */ + case REQ_Dx: /* mdoc(7) */ + { + bool parsable=true; + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + if (request==REQ_At) + { + out_html("AT&T UNIX "); + parsable=false; + } + else if (request==REQ_Fx) + { + out_html("FreeBSD "); + parsable=false; + } + else if (request==REQ_Nx) + out_html("NetBSD "); + else if (request==REQ_Ox) + out_html("OpenBSD "); + else if (request==REQ_Bx) + out_html("BSD "); + else if (request==REQ_Ux) + out_html("UNIX "); + else if (request==REQ_Dx) + out_html("DragonFly "); + if (parsable) + c=scan_troff_mandoc(c,1,0); + else + c=scan_troff(c,1,0); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Dl: /* mdoc(7) */ + { + c=c+j; + out_html(NEWLINE); + out_html("
    "); + if (*c=='\n') c++; + c=scan_troff_mandoc(c, 1, NULL); + out_html("
    "); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Bd: /* mdoc(7) */ + { /* Seems like a kind of example/literal mode */ + char bd_options[NULL_TERMINATED(MED_STR_MAX)]; + char *nl = strchr(c,'\n'); + c=c+j; + if (nl) + strlimitcpy(bd_options, c, nl - c, MED_STR_MAX); + out_html(NEWLINE); + mandoc_bd_options = 0; /* Remember options for terminating Bl */ + if (strstr(bd_options, "-offset indent")) + { + mandoc_bd_options |= BD_INDENT; + out_html("
    \n"); + } + if ( strstr(bd_options, "-literal") || strstr(bd_options, "-unfilled")) + { + if (fillout) + { + mandoc_bd_options |= BD_LITERAL; + out_html(set_font("R")); + out_html(change_to_size('0')); + out_html("
    \n");
    +                        }
    +                        curpos=0;
    +                        fillout=0;
    +                    }
    +                    c=skip_till_newline(c);
    +                    break;
    +                }
    +                case REQ_Ed:	/* mdoc(7) */
    +                {
    +                    if (mandoc_bd_options & BD_LITERAL)
    +                    {
    +                        if (!fillout)
    +                        {
    +                            out_html(set_font("R"));
    +                            out_html(change_to_size('0'));
    +                            out_html("
    \n"); + } + } + if (mandoc_bd_options & BD_INDENT) + out_html("
    \n"); + curpos=0; + fillout=1; + c=skip_till_newline(c); + break; + } + case REQ_Be: /* mdoc(7) */ + { + c=c+j; + if (fillout) + out_html("

    "); + else + { + out_html(NEWLINE); + } + curpos=0; + c=skip_till_newline(c); + break; + } + case REQ_Xr: /* mdoc(7) */ // ### FIXME: it should issue a directly + { + /* Translate xyz 1 to xyz(1) + * Allow for multiple spaces. Allow the section to be missing. + */ + char buff[NULL_TERMINATED(MED_STR_MAX)]; + char *bufptr; + trans_char(c,'"','\a'); + bufptr = buff; + c = c+j; + if (*c == '\n') c++; /* Skip spaces */ + while (isspace(*c) && *c != '\n') c++; + while (isalnum(*c) || *c == '.' || *c == ':' || *c == '_' || *c == '-') + { + /* Copy the xyz part */ + *bufptr = *c; + bufptr++; + if (bufptr >= buff + MED_STR_MAX) break; + c++; + } + while (isspace(*c) && *c != '\n') c++; /* Skip spaces */ + if (isdigit(*c)) + { + /* Convert the number if there is one */ + *bufptr = '('; + bufptr++; + if (bufptr < buff + MED_STR_MAX) + { + while (isalnum(*c)) + { + *bufptr = *c; + bufptr++; + if (bufptr >= buff + MED_STR_MAX) break; + c++; + } + if (bufptr < buff + MED_STR_MAX) + { + *bufptr = ')'; + bufptr++; + } + } + } + while (*c != '\n') + { + /* Copy the remainder */ + if (!isspace(*c)) + { + *bufptr = *c; + bufptr++; + if (bufptr >= buff + MED_STR_MAX) break; + } + c++; + } + *bufptr = '\n'; + bufptr[1] = 0; + scan_troff_mandoc(buff, 1, NULL); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Fl: // mdoc(7) "FLags" + { + trans_char(c,'"','\a'); + c+=j; + sl=fill_words(c, wordlist, &words, true, &c); + out_html(set_font("B")); + if (!words) + { + out_html("-"); // stdin or stdout + } + else + { + for (i=0;i0) + out_html(" "); // Put a space between flags + out_html("-"); + scan_troff_mandoc(wordlist[i], 1, NULL); + } + } + } + out_html(set_font("R")); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Pa: /* mdoc(7) */ + case REQ_Pf: /* mdoc(7) */ + { + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + c=scan_troff_mandoc(c, 1, NULL); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Pp: /* mdoc(7) */ + { + if (fillout) + out_html("

    \n"); + else + { + out_html(NEWLINE); + } + curpos=0; + c=skip_till_newline(c); + break; + } + case REQ_Aq: // mdoc(7) "Angle bracket Quote" + c=process_quote(c,j,"<",">"); + break; + case REQ_Bq: // mdoc(7) "Bracket Quote" + c=process_quote(c,j,"[","]"); + break; + case REQ_Dq: // mdoc(7) "Double Quote" + c=process_quote(c,j,"“","”"); + break; + case REQ_Pq: // mdoc(7) "Parenthese Quote" + c=process_quote(c,j,"(",")"); + break; + case REQ_Qq: // mdoc(7) "straight double Quote" + c=process_quote(c,j,""","""); + break; + case REQ_Sq: // mdoc(7) "Single Quote" + c=process_quote(c,j,"‘","’"); + break; + case REQ_Op: /* mdoc(7) */ + { + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + out_html(set_font("R")); + out_html("["); + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html("]"); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Oo: /* mdoc(7) */ + { + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + out_html(set_font("R")); + out_html("["); + c=scan_troff_mandoc(c, 1, NULL); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Oc: /* mdoc(7) */ + { + trans_char(c,'"','\a'); + c=c+j; + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html("]"); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Ql: /* mdoc(7) */ + { + /* Single quote first word in the line */ + char *sp; + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + sp = c; + do + { + /* Find first whitespace after the + * first word that isn't a mandoc macro + */ + while (*sp && isspace(*sp)) sp++; + while (*sp && !isspace(*sp)) sp++; + } while (*sp && isupper(*(sp-2)) && islower(*(sp-1))); + + /* Use a newline to mark the end of text to + * be quoted + */ + if (*sp) *sp = '\n'; + out_html("`"); /* Quote the text */ + c=scan_troff_mandoc(c, 1, NULL); + out_html("'"); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Ar: /* mdoc(7) */ + { + /* parse one line in italics */ + out_html(set_font("I")); + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') + { + /* An empty Ar means "file ..." */ + out_html("file ..."); + } + else + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Em: /* mdoc(7) */ + { + out_html(""); + trans_char(c,'"','\a'); + c+=j; + if (*c=='\n') c++; + c=scan_troff_mandoc(c, 1, NULL); + out_html(""); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Ad: /* mdoc(7) */ + case REQ_Va: /* mdoc(7) */ + case REQ_Xc: /* mdoc(7) */ + { + /* parse one line in italics */ + out_html(set_font("I")); + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Nd: /* mdoc(7) */ + { + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + out_html(" - "); + c=scan_troff_mandoc(c, 1, NULL); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Nm: // mdoc(7) "Name Macro" ### FIXME + { + static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = ""; // ### TODO Use QCString + trans_char(c,'"','\a'); + c=c+j; + + if (mandoc_synopsis && mandoc_name_count) + { + /* Break lines only in the Synopsis. + * The Synopsis section seems to be treated + * as a special case - Bummer! + */ + out_html("
    "); + } + else if (!mandoc_name_count) + { + char *nextbreak = strchr(c, '\n'); + char *nextspace = strchr(c, ' '); + if (nextspace < nextbreak) + nextbreak = nextspace; + + if (nextbreak) + { + /* Remember the name for later. */ + strlimitcpy(mandoc_name, c, nextbreak - c, SMALL_STR_MAX); + } + } + mandoc_name_count++; + + out_html(set_font("B")); + // ### FIXME: fill_words must be used + while (*c == ' '|| *c == '\t') c++; + if ((tolower(*c) >= 'a' && tolower(*c) <= 'z' ) || (*c >= '0' && *c <= '9')) + { + // alphanumeric argument + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html(NEWLINE); + } + else + { + /* If Nm has no argument, use one from an earlier + * Nm command that did have one. Hope there aren't + * too many commands that do this. + */ + out_html(mandoc_name); + out_html(set_font("R")); + } + + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_Cd: /* mdoc(7) */ + case REQ_Cm: /* mdoc(7) */ + case REQ_Ic: /* mdoc(7) */ + case REQ_Ms: /* mdoc(7) */ + case REQ_Or: /* mdoc(7) */ + case REQ_Sy: /* mdoc(7) */ + { + /* parse one line in bold */ + out_html(set_font("B")); + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + // ### FIXME: punctuation is handled badly! + case REQ_Dv: /* mdoc(7) */ + case REQ_Ev: /* mdoc(7) */ + case REQ_Fr: /* mdoc(7) */ + case REQ_Li: /* mdoc(7) */ + case REQ_No: /* mdoc(7) */ + case REQ_Ns: /* mdoc(7) */ + case REQ_Tn: /* mdoc(7) */ + case REQ_nN: /* mdoc(7) */ + { + trans_char(c,'"','\a'); + c=c+j; + if (*c=='\n') c++; + out_html(set_font("B")); + c=scan_troff_mandoc(c, 1, NULL); + out_html(set_font("R")); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_perc_A: /* mdoc(7) biblio stuff */ + case REQ_perc_D: + case REQ_perc_N: + case REQ_perc_O: + case REQ_perc_P: + case REQ_perc_Q: + case REQ_perc_V: + { + c=c+j; + if (*c=='\n') c++; + c=scan_troff(c, 1, NULL); /* Don't allow embedded mandoc coms */ + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_perc_B: + case REQ_perc_J: + case REQ_perc_R: + case REQ_perc_T: + { + c=c+j; + out_html(set_font("I")); + if (*c=='\n') c++; + c=scan_troff(c, 1, NULL); /* Don't allow embedded mandoc coms */ + out_html(set_font("R")); + if (fillout) + curpos++; + else + curpos=0; + break; + } + case REQ_UR: // ### FIXME man(7) "URl" + { + ignore_links=true; + c+=j; + char* newc; + h=fill_words(c, wordlist, &words, false, &newc); + *h=0; + if (words>0) + { + h=wordlist[0]; + // A parameter : means that we do not want an URL, not here and not until .UE + ur_ignore=(!qstrcmp(h,":")); + } + else + { + // We cannot find the URL, assume : + ur_ignore=true; + h=0; + } + if (!ur_ignore && words>0) + { + out_html("
    "); + } + c=newc; // Go to next line + break; + } + case REQ_UE: // ### FIXME man(7) "Url End" + { + c+=j; + c = skip_till_newline(c); + if (!ur_ignore) + { + out_html(""); + } + ur_ignore=false; + ignore_links=false; + break; + } + case REQ_UN: // ### FIXME man(7) "Url Named anchor" + { + c+=j; + char* newc; + h=fill_words(c, wordlist, &words, false, &newc); + *h=0; + if (words>0) + { + h=wordlist[0]; + out_html(""); + out_html(h); + out_html("\" id=\""); + out_html(h); + out_html("\">"); + } + c=newc; + break; + } + case REQ_nroff: // groff(7) "NROFF mode" + mode = true; + case REQ_troff: // groff(7) "TROFF mode" + { + s_nroff = mode; + c+=j; + c = skip_till_newline(c); + } + case REQ_als: // groff(7) "ALias String" + { + /* + * Note an alias is supposed to be something like a hard link + * However to make it simplier, we only copy the string. + */ + // Be careful: unlike .rn, the destination is first, origin is second + kdDebug(7107) << "start .als" << endl; + c+=j; + const TQCString name ( scan_identifier( c ) ); + if ( name.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty destination string to alias" << endl; + break; + } + while (*c && isspace(*c) && *c!='\n') ++c; + const TQCString name2 ( scan_identifier ( c ) ); + if ( name2.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty origin string to alias" << endl; + break; + } + kdDebug(7107) << "Alias " << name2 << " to " << name << endl; + c=skip_till_newline(c); + if ( name == name2 ) + { + kdDebug(7107) << "EXCEPTION: same origin and destination string to alias: " << name << endl; + break; + } + // Second parametr is origin (unlike in .rn) + TQMap::iterator it=s_stringDefinitionMap.find(name2); + if (it==s_stringDefinitionMap.end()) + { + kdDebug(7107) << "EXCEPTION: cannot find string to make alias: " << name2 << endl; + } + else + { + StringDefinition def=(*it); + s_stringDefinitionMap.insert(name,def); + } + kdDebug(7107) << "end .als" << endl; + break; + } + case REQ_rr: // groff(7) "Remove number Register" + { + kdDebug(7107) << "start .rr" << endl; + c += j; + const TQCString name ( scan_identifier( c ) ); + if ( name.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty origin string to remove/rename: " << endl; + break; + } + c = skip_till_newline( c ); + TQMap ::iterator it = s_numberDefinitionMap.find( name ); + if ( it == s_numberDefinitionMap.end() ) + { + kdDebug(7107) << "EXCEPTION: trying to remove inexistant number register: " << endl; + } + else + { + s_numberDefinitionMap.remove( name ); + } + kdDebug(7107) << "end .rr" << endl; + break; + } + case REQ_rnn: // groff(7) "ReName Number register" + { + kdDebug(7107) << "start .rnn" << endl; + c+=j; + const TQCString name ( scan_identifier ( c ) ); + if ( name.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty origin to remove/rename number register" << endl; + break; + } + while (*c && isspace(*c) && *c!='\n') ++c; + const TQCString name2 ( scan_identifier ( c ) ); + if ( name2.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty destination to rename number register " << endl; + break; + } + c = skip_till_newline( c ); + TQMap::iterator it=s_numberDefinitionMap.find(name); + if (it==s_numberDefinitionMap.end()) + { + kdDebug(7107) << "EXCEPTION: cannot find number register to rename: " << name << endl; + } + else + { + NumberDefinition def=(*it); + s_numberDefinitionMap.remove(name); // ### QT4: removeAll + s_numberDefinitionMap.insert(name2,def); + } + kdDebug(7107) << "end .rnn" << endl; + break; + } + case REQ_aln: // groff(7) "ALias Number Register" + { + /* + * Note an alias is supposed to be something like a hard link + * However to make it simplier, we only copy the string. + */ + // Be careful: unlike .rnn, the destination is first, origin is second + kdDebug(7107) << "start .aln" << endl; + c+=j; + const TQCString name ( scan_identifier( c ) ); + if ( name.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty destination number register to alias" << endl; + break; + } + while (*c && isspace(*c) && *c!='\n') ++c; + const TQCString name2 ( scan_identifier( c ) ); + if ( name2.isEmpty() ) + { + kdDebug(7107) << "EXCEPTION: empty origin number register to alias" << endl; + break; + } + kdDebug(7107) << "Alias " << name2 << " to " << name << endl; + c = skip_till_newline( c ); + if ( name == name2 ) + { + kdDebug(7107) << "EXCEPTION: same origin and destination number register to alias: " << name << endl; + break; + } + // Second parametr is origin (unlike in .rnn) + TQMap::iterator it=s_numberDefinitionMap.find(name2); + if (it==s_numberDefinitionMap.end()) + { + kdDebug(7107) << "EXCEPTION: cannot find string to make alias: " << name2 << endl; + } + else + { + NumberDefinition def=(*it); + s_numberDefinitionMap.insert(name,def); + } + kdDebug(7107) << "end .aln" << endl; + break; + } + case REQ_shift: // groff(7) "SHIFT parameter" + { + c+=j; + h=c; + while (*h && *h!='\n' && isdigit(*h) ) ++h; + const char tempchar = *h; + *h = 0; + const TQCString number = c; + *h = tempchar; + c = skip_till_newline( h ); + unsigned int result = 1; // Numbers of shifts to do + if ( !number.isEmpty() ) + { + bool ok = false; + result = number.toUInt(&ok); + if ( !ok || result < 1 ) + result = 1; + } + for ( unsigned int num = 0; num < result; ++num ) + { + if ( !s_argumentList.isEmpty() ) + s_argumentList.pop_front(); + } + break; + } + case REQ_while: // groff(7) "WHILE loop" + { + request_while( c, j, mandoc_command ); + break; + } + case REQ_do: // groff(7) "DO command" + { + // HACK: we just replace do by a \n and a . + *c = '\n'; + c++; + *c = '.'; + // The . will be treated as next character + break; + } + default: + { + if (mandoc_command && + ((isupper(*c) && islower(*(c+1))) + || (islower(*c) && isupper(*(c+1)))) ) + { + /* Let through any mdoc(7) commands that haven't + * been delt with. + * I don't want to miss anything out of the text. + */ + char buf[4] = { c[0], c[1], ' ', 0 }; + out_html(buf); /* Print the command (it might just be text). */ + c=c+j; + trans_char(c,'"','\a'); + if (*c=='\n') c++; + out_html(set_font("R")); + c=scan_troff(c, 1, NULL); + out_html(NEWLINE); + if (fillout) + curpos++; + else + curpos=0; + } + else + c=skip_till_newline(c); + break; + } + } + } + } + if (fillout) + { + out_html(NEWLINE); + curpos++; + } + return c; +} + +static int contained_tab=0; +static bool mandoc_line=false; /* Signals whether to look for embedded mandoc + * commands. + */ + +static char *scan_troff(char *c, bool san, char **result) +{ /* san : stop at newline */ + char *h; + char intbuff[NULL_TERMINATED(MED_STR_MAX)]; + int ibp=0; +#define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; } + char *exbuffer; + int exbuffpos, exbuffmax, exnewline_for_fun; + bool exscaninbuff; + int usenbsp=0; + + exbuffer=buffer; + exbuffpos=buffpos; + exbuffmax=buffmax; + exnewline_for_fun=newline_for_fun; + exscaninbuff=scaninbuff; + newline_for_fun=0; + if (result) { + if (*result) { + buffer=*result; + buffpos=tqstrlen(buffer); + buffmax=buffpos; + } else { + buffer = stralloc(LARGE_STR_MAX); + buffpos=0; + buffmax=LARGE_STR_MAX; + } + scaninbuff=true; + } + h=c; // ### FIXME below are too many tests that may go before the posiiton of c + /* start scanning */ + + // ### VERIFY: a dot must be at first position, we cannot add newlines or it would allow spaces before a dot + while (*h == ' ') + { +#if 1 + ++h; +#else + *h++ = '\n'; +#endif + } + + while (h && *h && (!san || newline_for_fun || *h!='\n')) { + + if (*h==escapesym) { + h++; + FLUSHIBP; + h = scan_escape(h); + } else if (*h==controlsym && h[-1]=='\n') { + h++; + FLUSHIBP; + h = scan_request(h); + if (h && san && h[-1]=='\n') h--; + } else if (mandoc_line + && ((*(h-1)) && (isspace(*(h-1)) || (*(h-1))=='\n')) + && *(h) && isupper(*(h)) + && *(h+1) && islower(*(h+1)) + && *(h+2) && isspace(*(h+2))) { + // mdoc(7) embedded command eg ".It Fl Ar arg1 Fl Ar arg2" + FLUSHIBP; + h = scan_request(h); + if (san && h[-1]=='\n') h--; + } else if (*h==nobreaksym && h[-1]=='\n') { + h++; + FLUSHIBP; + h = scan_request(h); + if (san && h[-1]=='\n') h--; + } else { + /* int mx; */ + if (still_dd && isalnum(*h) && h[-1]=='\n') { + /* sometimes a .HP request is not followed by a .br request */ + FLUSHIBP; + out_html("
    "); + curpos=0; + still_dd=false; + } + switch (*h) { + case '&': + intbuff[ibp++]='&'; + intbuff[ibp++]='a'; + intbuff[ibp++]='m'; + intbuff[ibp++]='p'; + intbuff[ibp++]=';'; + curpos++; + break; + case '<': + intbuff[ibp++]='&'; + intbuff[ibp++]='l'; + intbuff[ibp++]='t'; + intbuff[ibp++]=';'; + curpos++; + break; + case '>': + intbuff[ibp++]='&'; + intbuff[ibp++]='g'; + intbuff[ibp++]='t'; + intbuff[ibp++]=';'; + curpos++; + break; + case '"': + intbuff[ibp++]='&'; + intbuff[ibp++]='q'; + intbuff[ibp++]='u'; + intbuff[ibp++]='o'; + intbuff[ibp++]='t'; + intbuff[ibp++]=';'; + curpos++; + break; + case '\n': + if (h != c && h[-1]=='\n' && fillout) { + intbuff[ibp++]='<'; + intbuff[ibp++]='P'; + intbuff[ibp++]='>'; + } + if (contained_tab && fillout) { + intbuff[ibp++]='<'; + intbuff[ibp++]='B'; + intbuff[ibp++]='R'; + intbuff[ibp++]='>'; + } + contained_tab=0; + curpos=0; + usenbsp=0; + intbuff[ibp++]='\n'; + break; + case '\t': + { + int curtab=0; + contained_tab=1; + FLUSHIBP; + /* like a typewriter, not like TeX */ + tabstops[19]=curpos+1; + while (curtab480) { FLUSHIBP; } + curpos++; + } + } else { + out_html(""); + while (curpos"); + } + } + } + break; + default: + if (*h==' ' && (h[-1]=='\n' || usenbsp)) { + FLUSHIBP; + if (!usenbsp && fillout) { + out_html("
    "); + curpos=0; + } + usenbsp=fillout; + if (usenbsp) out_html(" "); else intbuff[ibp++]=' '; + } else if (*h>31 && *h<127) intbuff[ibp++]=*h; + else if (((unsigned char)(*h))>127) { + intbuff[ibp++]=*h; + } + curpos++; + break; + } + if (ibp > (MED_STR_MAX - 20)) FLUSHIBP; + h++; + } + } + FLUSHIBP; + if (buffer) buffer[buffpos]='\0'; + if (san && h && *h) h++; + newline_for_fun=exnewline_for_fun; + if (result) { + *result = buffer; + buffer=exbuffer; + buffpos=exbuffpos; + buffmax=exbuffmax; + scaninbuff=exscaninbuff; + } + + return h; +} + + +static char *scan_troff_mandoc(char *c, bool san, char **result) +{ + char *ret; + char *end = c; + bool oldval = mandoc_line; + mandoc_line = true; + while (*end && *end != '\n') { + end++; + } + + if (end > c + 2 + && ispunct(*(end - 1)) + && isspace(*(end - 2)) && *(end - 2) != '\n') { + /* Don't format lonely punctuation E.g. in "xyz ," format + * the xyz and then append the comma removing the space. + */ + *(end - 2) = '\n'; + ret = scan_troff(c, san, result); + *(end - 2) = *(end - 1); + *(end - 1) = ' '; + } + else { + ret = scan_troff(c, san, result); + } + mandoc_line = oldval; + return ret; +} + +// Entry point +void scan_man_page(const char *man_page) +{ + if (!man_page) + return; + + kdDebug(7107) << "Start scanning man page" << endl; + + // ## Do more init + // Unlike man2html, we actually call this several times, hence the need to + // properly cleanup all those static vars + s_ifelseval.clear(); + + s_characterDefinitionMap.clear(); + InitCharacterDefinitions(); + + s_stringDefinitionMap.clear(); + InitStringDefinitions(); + + s_numberDefinitionMap.clear(); + InitNumberDefinitions(); + + s_argumentList.clear(); + + section = 0; + + s_dollarZero = ""; // No macro called yet! + + output_possible = false; + int strLength = tqstrlen(man_page); + char *buf = new char[strLength + 2]; + qstrcpy(buf+1, man_page); + buf[0] = '\n'; + + kdDebug(7107) << "Parse man page" << endl; + + scan_troff(buf+1,0,NULL); + + kdDebug(7107) << "Man page parsed!" << endl; + + while (itemdepth || dl_set[itemdepth]) { + out_html("\n"); + if (dl_set[itemdepth]) dl_set[itemdepth]=0; + else if (itemdepth > 0) itemdepth--; + } + + out_html(set_font("R")); + out_html(change_to_size(0)); + if (!fillout) { + fillout=1; + out_html(""); + } + out_html(NEWLINE); + + if (section) { + output_real("
    \n"); + section = 0; + } + + if (output_possible) { + output_real("
    \n"); + output_real("
    \n"); + output_real("
    \n"); + output_real("\"Bottom\n"); + output_real("
    \n"); + output_real("
    \n"); + output_real("\"Bottom\n"); + output_real("
    \n"); + output_real("
    \n"); + + output_real("\n\n"); + } + delete [] buf; + + // Release memory + s_characterDefinitionMap.clear(); + s_stringDefinitionMap.clear(); + s_numberDefinitionMap.clear(); + s_argumentList.clear(); + + // reinit static variables for reuse + delete [] buffer; + buffer = 0; + + escapesym='\\'; + nobreaksym='\''; + controlsym='.'; + fieldsym=0; + padsym=0; + + buffpos=0; + buffmax=0; + scaninbuff=false; + itemdepth=0; + for (int i = 0; i < 20; i++) + dl_set[i] = 0; + still_dd=false; + for (int i = 0; i < 12; i++) + tabstops[i] = (i+1)*8; + maxtstop=12; + curpos=0; + + mandoc_name_count = 0; +} + +#ifdef SIMPLE_MAN2HTML +void output_real(const char *insert) +{ + cout << insert; +} + +char *read_man_page(const char *filename) +{ + int man_pipe = 0; + char *man_buf = NULL; + + FILE *man_stream = NULL; + struct stat stbuf; + size_t buf_size; + if (stat(filename, &stbuf) == -1) { + std::cerr << "read_man_page: can't find " << filename << endl; + return NULL; + } + if (!S_ISREG(stbuf.st_mode)) { + std::cerr << "read_man_page: no file " << filename << endl; + return NULL; + } + buf_size = stbuf.st_size; + man_buf = stralloc(buf_size+5); + man_pipe = 0; + man_stream = fopen(filename, "r"); + if (man_stream) { + man_buf[0] = '\n'; + if (fread(man_buf+1, 1, buf_size, man_stream) == buf_size) { + man_buf[buf_size] = '\n'; + man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0'; + } + else { + man_buf = NULL; + } + fclose(man_stream); + } + return man_buf; +} + +int main(int argc, char **argv) +{ + htmlPath = "."; + cssPath = "."; + if (argc < 2) { + std::cerr << "call: " << argv[0] << " \n"; + return 1; + } + if (chdir(argv[1])) { + char *buf = read_man_page(argv[1]); + if (buf) { + scan_man_page(buf); + delete [] buf; + } + } else { + DIR *dir = opendir("."); + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + cerr << "converting " << ent->d_name << endl; + char *buf = read_man_page(ent->d_name); + if (buf) { + scan_man_page(buf); + delete [] buf; + } + } + closedir(dir); + } + return 0; +} + + +#endif diff --git a/tdeioslave/man/man2html.h b/tdeioslave/man/man2html.h new file mode 100644 index 000000000..68b01efe9 --- /dev/null +++ b/tdeioslave/man/man2html.h @@ -0,0 +1,34 @@ +/** + * \file man2html.h + * + * \note Despite that this file is installed publically, it should not be included + * \todo ### KDE4: make this file private + * + */ + +#include + +/** call this with the buffer you have */ +void scan_man_page(const char *man_page); + +/** + * Set the paths to TDE resources + * + * \param htmlPath Path to the TDE resources, encoded for HTML + * \param cssPath Path to the TDE resources, encoded for CSS + * \since 3.5 + * + */ +extern void setResourcePath(const TQCString& _htmlPath, const TQCString& _cssPath); + +/** implement this somewhere. It will be called + with HTML contents +*/ +extern void output_real(const char *insert); + +/** + * called for requested man pages. filename can be a + * relative path! Return NULL on errors. The returned + * char array is freed by man2html + */ +extern char *read_man_page(const char *filename); -- cgit v1.2.1