/* 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 "kio_man.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kio_man.moc" #include "man2html.h" #include #include #include using namespace KIO; MANProtocol *MANProtocol::_self = 0; #define SGML2ROFF_DIRS "/usr/lib/sgml" /* * Drop trailing ".section[.gz]" from name */ static void stripExtension( QString *name ) { int pos = name->length(); if ( name->indexOf(".gz", -3) != -1 ) pos -= 3; else if ( name->indexOf(".z", -2, Qt::CaseInsensitive) != -1 ) pos -= 2; else if ( name->indexOf(".bz2", -4) != -1 ) pos -= 4; else if ( name->indexOf(".bz", -3) != -1 ) pos -= 3; else if ( name->indexOf(".lzma", -5) != -1 ) pos -= 5; else if ( name->indexOf(".xz", -3) != -1 ) pos -= 3; if ( pos > 0 ) pos = name->lastIndexOf('.', pos-1); if ( pos > 0 ) name->truncate( pos ); } static bool parseUrl(const QString& _url, QString &title, QString §ion) { section.clear(); QString url = _url; url = url.trimmed(); if (url.isEmpty() || url.at(0) == '/') { if (url.isEmpty() || KStandardDirs::exists(url)) { // man:/usr/share/man/man1/ls.1.gz is a valid file title = url; return true; } else { // If the directory does not exist, then it is perhaps a normal man page kDebug(7107) << url << " does not exist"; } } while (!url.isEmpty() && url.at(0) == '/') url.remove(0,1); title = url; int pos = url.indexOf('('); if (pos < 0) return true; // man:ls -> title=ls title = title.left(pos); section = url.mid(pos+1); pos = section.indexOf(')'); if (pos >= 0) { if (pos < section.length() - 2 && title.isEmpty()) { title = section.mid(pos + 2); } section = section.left(pos); } // man:ls(2) -> title="ls", section="2" return true; } MANProtocol::MANProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) : QObject(), SlaveBase("man", pool_socket, app_socket) { assert(!_self); _self = this; const QString common_dir = KGlobal::dirs()->findResourceDir( "html", "en/common/kde-default.css" ); const QString strPath=QString( "file:%1/en/common" ).arg( common_dir ); m_cssPath=strPath.toLocal8Bit(); // ### TODO encode for CSS section_names << "0" << "0p" << "1" << "1p" << "2" << "3" << "3n" << "3p" << "4" << "5" << "6" << "7" << "8" << "9" << "l" << "n"; QString cssPath(KStandardDirs::locate( "data", "kio_docfilter/kio_docfilter.css" )); KUrl cssUrl(KUrl::fromPath(cssPath)); m_manCSSFile = cssUrl.url().toUtf8(); } MANProtocol *MANProtocol::self() { return _self; } MANProtocol::~MANProtocol() { _self = 0; } void MANProtocol::parseWhatIs( QMap &i, QTextStream &t, const QString &mark ) { QRegExp re( mark ); QString l; while ( !t.atEnd() ) { l = t.readLine(); int pos = re.indexIn( l ); if (pos != -1) { QString names = l.left(pos); QString descr = l.mid(pos + re.matchedLength()); while ((pos = names.indexOf(",")) != -1) { i[names.left(pos++)] = descr; while (names[pos] == ' ') pos++; names = names.mid(pos); } i[names] = descr; } } } bool MANProtocol::addWhatIs(QMap &i, const QString &name, const QString &mark) { QFile f(name); if (!f.open(QIODevice::ReadOnly)) return false; QTextStream t(&f); parseWhatIs( i, t, mark ); return true; } QMap MANProtocol::buildIndexMap(const QString §ion) { QMap i; QStringList man_dirs = manDirectories(); // Supplementary places for whatis databases man_dirs += m_mandbpath; if (!man_dirs.contains("/var/cache/man")) man_dirs << "/var/cache/man"; if (!man_dirs.contains("/var/catman")) man_dirs << "/var/catman"; QStringList names; names << "whatis.db" << "whatis"; QString mark = "\\s+\\(" + section + "[a-z]*\\)\\s+-\\s+"; for ( QStringList::ConstIterator it_dir = man_dirs.constBegin(); it_dir != man_dirs.constEnd(); ++it_dir ) { if ( QFile::exists( *it_dir ) ) { QStringList::ConstIterator it_name; for ( it_name = names.constBegin(); it_name != names.constEnd(); it_name++ ) { if (addWhatIs(i, (*it_dir) + '/' + (*it_name), mark)) break; } if ( it_name == names.constEnd() ) { KProcess proc; proc << "whatis" << "-M" << (*it_dir) << "-w" << "*"; proc.setOutputChannelMode( KProcess::OnlyStdoutChannel ); proc.execute(); QTextStream t( proc.readAllStandardOutput(), QIODevice::ReadOnly ); parseWhatIs( i, t, mark ); } } } return i; } QStringList MANProtocol::manDirectories() { checkManPaths(); // // Build a list of man directories including translations // QStringList man_dirs; for ( QStringList::ConstIterator it_dir = m_manpath.constBegin(); it_dir != m_manpath.constEnd(); it_dir++ ) { // Translated pages in "/" if the directory // exists QStringList languages = KGlobal::locale()->languageList(); for (QStringList::ConstIterator it_lang = languages.constBegin(); it_lang != languages.constEnd(); it_lang++ ) { if ( !(*it_lang).isEmpty() && (*it_lang) != QString("C") ) { QString dir = (*it_dir) + '/' + (*it_lang); struct stat sbuf; if ( ::stat( QFile::encodeName( dir ), &sbuf ) == 0 && S_ISDIR( sbuf.st_mode ) ) { const QString p = QDir(dir).canonicalPath(); if (!man_dirs.contains(p)) man_dirs += p; } } } // Untranslated pages in "" const QString p = QDir(*it_dir).canonicalPath(); if (!man_dirs.contains(p)) man_dirs += p; } return man_dirs; } QStringList MANProtocol::findPages(const QString &_section, const QString &title, bool full_path) { QString section = _section; QStringList list; // kDebug() << "findPages '" << section << "' '" << title << "'\n"; if ( (!title.isEmpty()) && (title.at(0) == '/') ) { list.append(title); return list; } const QString star( "*" ); // // Find man sections in this directory // QStringList sect_list; if ( section.isEmpty() ) section = star; if ( section != star ) { // // Section given as argument // sect_list += section; while ( (!section.isEmpty()) && (section.at(section.length() - 1).isLetter()) ) { section.truncate(section.length() - 1); sect_list += section; } } else { sect_list += section; } QStringList man_dirs = manDirectories(); // // Find man pages in the sections listed above // for ( int i=0;id_name ); QString sect; if ( file.startsWith( man ) ) sect = file.mid(3); else if (file.startsWith(sman)) sect = file.mid(4); if (sect.toLower()==it_real) it_real = sect; // Only add sect if not already contained, avoid duplicates if (!sect_list.contains(sect) && _section.isEmpty()) { //kDebug() << "another section " << sect; sect_list += sect; } } //kDebug(7107) <<" after while loop"; ::closedir( dp ); #if 0 kDebug(7107)<<"================-"; kDebug(7107)<<"star=="<d_name[0] != '.' ) { QString name = QFile::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... QString 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.write(insert,strlen(insert)); } if (!insert || m_outputBuffer.pos() >= 2048) { m_outputBuffer.close(); data(m_outputBuffer.buffer()); m_outputBuffer.setData(QByteArray()); m_outputBuffer.open(QIODevice::WriteOnly); } } #ifndef SIMPLE_MAN2HTML // called by man2html extern char *read_man_page(const char *filename) { return MANProtocol::self()->readManPage(filename); } // called by man2html extern void output_real(const char *insert) { MANProtocol::self()->output(insert); } #endif void MANProtocol::get(const KUrl& url ) { kDebug(7107) << "GET " << url.url(); QString title, section; if (!parseUrl(url.path(), title, section)) { showMainIndex(); return; } // tell the mimetype mimeType("text/html"); // see if an index was requested if (url.query().isEmpty() && (title.isEmpty() || title == "/" || title == ".")) { if (section == "index" || section.isEmpty()) showMainIndex(); else showIndex(section); return; } const QStringList 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.
" "Check that you have typed the name using the correct upper and lower case characters.
" "If everything looks correct, then you may need to improve the search path " "for man pages; either using the environment variable MANPATH or using a matching file " "in the /etc directory.", Qt::escape(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) && ((QString(foundPages[0]+".gz") == foundPages[1]) || (foundPages[0] == QString(foundPages[1]+".gz")))) pageFound=true; else outputMatchingPages(foundPages); } //yes, we found exactly one man page if (pageFound) { setResourcePath(m_cssPath); setCssFile(m_manCSSFile); m_outputBuffer.open(QIODevice::WriteOnly); const QByteArray filename=QFile::encodeName(foundPages[0]); char *buf = readManPage(filename); if (!buf) { outputError(i18n("Open of %1 failed.", 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.setData(QByteArray()); // tell we are done data(QByteArray()); } finished(); } //--------------------------------------------------------------------- char *MANProtocol::readManPage(const char *_filename) { QByteArray filename = _filename; QByteArray array; /* 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). */ //QString file_mimetype = KMimeType::findByPath(QString(filename), 0, false)->name(); if (QString(filename).contains("sman", Qt::CaseInsensitive)) //file_mimetype == "text/html" || ) { KProcess proc; // Determine path to sgml2roff, if not already done. getProgramPath(); proc << mySgml2RoffPath << filename; proc.setOutputChannelMode( KProcess::OnlyStdoutChannel ); proc.execute(); array = proc.readAllStandardOutput(); } else { if (QDir::isRelativePath(filename)) { kDebug(7107) << "relative " << filename; filename = QDir::cleanPath(lastdir + '/' + filename).toUtf8(); kDebug(7107) << "resolved to " << filename; } lastdir = filename.left(filename.lastIndexOf('/')); if ( !QFile::exists(QFile::decodeName(filename)) ) // if given file does not exist, find with suffix { kDebug(7107) << "not existing " << filename; QDir mandir(lastdir); mandir.setNameFilters(QStringList() << (filename.mid(filename.lastIndexOf('/') + 1) + ".*")); filename = lastdir + '/' + QFile::encodeName(mandir.entryList().first()); kDebug(7107) << "resolved to " << filename; } QIODevice *fd = KFilterDev::deviceForFile(filename); if ( !fd || !fd->open(QIODevice::ReadOnly)) { delete fd; return 0; } array = fd->readAll(); kDebug(7107) << "read " << array.size(); fd->close(); delete fd; } if (array.isEmpty()) return 0; // as we do not know in which encoding the man source is, try to automatically // detect it and always return it as UTF-8 KEncodingProber encodingProber; encodingProber.feed(array); kDebug(7107) << "auto-detect encoding for" << filename << "guess=" << encodingProber.encoding() << "confidence=" << encodingProber.confidence(); QString out = QTextCodec::codecForName(encodingProber.encoding())->toUnicode(array); array = out.toUtf8(); const int len = array.size(); char *buf = new char[len + 4]; memmove(buf + 1, array.data(), len); buf[0] = buf[len+1] = '\n'; // Start and end with an end of line buf[len+2] = buf[len+3] = '\0'; // Two NUL characters at end return buf; } //--------------------------------------------------------------------- void MANProtocol::outputError(const QString& errmsg) { QByteArray array; QTextStream os(&array, QIODevice::WriteOnly); os.setCodec( "UTF-8" ); os << "" << endl; os << "" << endl; os << "" << i18n("Man output") << "\n" << endl; if ( !m_manCSSFile.isEmpty() ) os << "" << endl; os << "" << endl; os << "" << i18n("

KDE Man Viewer Error

") << errmsg << "" << endl; os << "" << endl; data(array); } void MANProtocol::outputMatchingPages(const QStringList &matchingPages) { QByteArray array; QTextStream os(&array, QIODevice::WriteOnly); os.setCodec( "UTF-8" ); 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 (QStringList::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 QString sectList = getenv("MANSECT"); QStringList sections; if (sectList.isEmpty()) sections = buildSectionList(manDirectories()); else sections = sectList.split( ':'); os << "" << endl; QSet accessKeys; char alternateAccessKey = 'a'; QStringList::ConstIterator it; for (it = sections.constBegin(); it != sections.constEnd(); ++it) { // create a unique access key QChar accessKey = (*it).at((*it).length() - 1); // rightmost char while ( accessKeys.contains(accessKey) ) accessKey = alternateAccessKey++; accessKeys.insert(accessKey); os << "" << endl; } os << "
" << i18n("Section %1", *it) << "  " << sectionName(*it) << "
" << endl; // print footer os << "" << endl; data(array); finished(); } void MANProtocol::constructPath(QStringList& constr_path, QStringList constr_catmanpath) { QMap manpath_map; QMap 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" QRegExp manpath_regex( "^MANPATH\\s" ); QRegExp mandatory_regex( "^MANDATORY_MANPATH\\s" ); QRegExp manpath_map_regex( "^MANPATH_MAP\\s" ); QRegExp mandb_map_regex( "^MANDB_MAP\\s" ); //QRegExp section_regex( "^SECTION\\s" ); QRegExp space_regex( "\\s+" ); // for parsing manpath map QFile mc("/etc/man.conf"); // Caldera if (!mc.exists()) mc.setFileName("/etc/manpath.config"); // SuSE, Debian if (!mc.exists()) mc.setFileName("/etc/man.config"); // Mandrake if (mc.open(QIODevice::ReadOnly)) { QTextStream is(&mc); is.setCodec( QTextCodec::codecForLocale () ); while (!is.atEnd()) { const QString line = is.readLine(); if ( manpath_regex.indexIn(line) == 0 ) { const QString path = line.mid(8).trimmed(); constr_path += path; } else if ( mandatory_regex.indexIn(line) == 0 ) { const QString path = line.mid(18).trimmed(); constr_path += path; } else if ( manpath_map_regex.indexIn(line) == 0 ) { // The entry is "MANPATH_MAP " const QStringList mapping = line.split( space_regex); if ( mapping.count() == 3 ) { const QString dir = QDir::cleanPath( mapping[1] ); const QString mandir = QDir::cleanPath( mapping[2] ); manpath_map[ dir ] = mandir; } } else if ( mandb_map_regex.indexIn(line) == 0 ) { // The entry is "MANDB_MAP " const QStringList mapping = line.split( space_regex); if ( mapping.count() == 3 ) { const QString mandir = QDir::cleanPath( mapping[1] ); const QString catmandir = QDir::cleanPath( 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).trimmed(); } */ } mc.close(); } // Default paths static const char * const 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.indexOf( QString( manpaths[i] ) ) == -1 ) constr_path += QString( 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 QStringList path = QString::fromLocal8Bit( ::getenv("PATH") ).split( ':', QString::SkipEmptyParts ); for ( QStringList::const_iterator it = path.constBegin(); it != path.constEnd(); ++it ) { const QString dir = QDir::cleanPath( *it ); QString mandir = manpath_map[ dir ]; if ( !mandir.isEmpty() ) { // a path mapping exists if ( constr_path.indexOf( mandir ) == -1 ) constr_path += mandir; } else { // no manpath mapping, use "/man" and "/../man" mandir = dir + QString( "/man" ); if ( constr_path.indexOf( mandir ) == -1 ) constr_path += mandir; int pos = dir.lastIndexOf( '/' ); if ( pos > 0 ) { mandir = dir.left( pos ) + QString("/man"); if ( constr_path.indexOf( mandir ) == -1 ) constr_path += mandir; } } QString catmandir = mandb_map[ mandir ]; if ( !mandir.isEmpty() ) { if ( constr_catmanpath.indexOf( catmandir ) == -1 ) constr_catmanpath += catmandir; } else { // What is the default mapping? catmandir = mandir; catmandir.replace("/usr/share/","/var/cache/"); if ( constr_catmanpath.indexOf( catmandir ) == -1 ) constr_catmanpath += catmandir; } } } } void MANProtocol::checkManPaths() { static bool inited = false; if (inited) return; inited = true; const QString manpath_env = QString::fromLocal8Bit( ::getenv("MANPATH") ); //QString mansect_env = QString::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 QStringList constr_path; QStringList constr_catmanpath; // catmanpath QString 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 QStringList path_list_env = manpath_env.split( ':', QString::KeepEmptyParts); for ( QStringList::const_iterator it = path_list_env.constBegin(); it != path_list_env.constEnd(); ++it ) { struct stat sbuf; QString dir = (*it); if ( !dir.isEmpty() ) { // Add dir to the man path if it exists if ( m_manpath.indexOf( dir ) == -1 ) { if ( ::stat( QFile::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 ( QStringList::const_iterator it2 = constr_path.constBegin(); it2 != constr_path.constEnd(); it2++ ) { dir = (*it2); if ( !dir.isEmpty() ) { if ( m_manpath.indexOf( dir ) == -1 ) { if ( ::stat( QFile::encodeName( dir ), &sbuf ) == 0 && S_ISDIR( sbuf.st_mode ) ) { m_manpath += dir; } } } } } } /* sections are not used // Sections QStringList m_mansect = mansect_env.split( ':', QString::KeepEmptyParts); const char* const 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.indexOf( QString( default_sect[i] ) ) == -1 ) m_mansect += QString( default_sect[i] ); */ } // 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; 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 = qstrnicmp( m1->manpage_begin, m2->manpage_begin, m2->manpage_len); if (!i) return 1; return i; } if ( m1->manpage_len < m2->manpage_len) { i = qstrnicmp( m1->manpage_begin, m2->manpage_begin, m1->manpage_len); if (!i) return -1; return i; } return qstrnicmp( m1->manpage_begin, m2->manpage_begin, m1->manpage_len); } void MANProtocol::showIndex(const QString& section) { QByteArray array_h; QTextStream os_h(&array_h, QIODevice::WriteOnly); os_h.setCodec( "UTF-8" ); // print header os_h << "" << endl; os_h << "" << endl; os_h << "" << i18n("UNIX Manual Index") << "" << endl; if ( !m_manCSSFile.isEmpty() ) os_h << "" << endl; os_h << "" << endl << "" << endl; QByteArray array_d; QTextStream os(&array_d, QIODevice::WriteOnly); os.setCodec( "UTF-8" ); os << "
" << endl; os << "

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

" << endl; // compose list of search paths ------------------------------------------------------------- checkManPaths(); infoMessage(i18n("Generating Index")); // search for the man pages QStringList pages = findPages( section, QString() ); if ( pages.count() == 0 ) // not a single page found { // print footer os << "
" << endl; infoMessage(QString()); data(array_h + array_d); finished(); return; } QMap indexmap = buildIndexMap(section); // print out the list os << "" << endl; int listlen = pages.count(); man_index_ptr *indexlist = new man_index_ptr[listlen]; listlen = 0; QStringList::const_iterator page; for (page = pages.constBegin(); page != pages.constEnd(); ++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).toUtf8()); 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; else if ( len >= 5 && strcmp( end-4, ".lzma" ) == 0 ) end -= 5; else if ( len >= 3 && strcmp( end-2, ".xz" ) == 0 ) end -= 3; 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) { indexlist[listlen] = manindex; listlen++; } else delete manindex; } // // 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; // sort and print qsort(indexlist, listlen, sizeof(struct man_index_t *), compare_man_index); QChar firstchar, tmp; QString indexLine="
\n"; if (indexlist[0]->manpage_len>0) { firstchar=QChar((indexlist[0]->manpage_begin)[0]).toLower(); const QString appendixstr = QString( " [%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 && !qstrncmp(last_index->manpage_begin, manindex->manpage_begin, manindex->manpage_len) ) { continue; } tmp=QChar((manindex->manpage_begin)[0]).toLower(); if (firstchar != tmp) { firstchar = tmp; os << "" << endl; const QString appendixstr = QString( " [%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; os << "
\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] : "" ) << "
" << endl; os << indexLine << endl; // print footer os << "" << endl; // set the links "toolbar" also at the top os_h << indexLine << endl; infoMessage(QString()); data(array_h + array_d); finished(); } void MANProtocol::listDir(const KUrl &url) { kDebug( 7107 ) << url; QString title; QString section; if ( !parseUrl(url.path(), title, section) ) { error( KIO::ERR_MALFORMED_URL, url.url() ); return; } // stat() and listDir() declared that everything is an html file. // However we can list man: and man:(1) as a directory (e.g. in dolphin). // But we cannot list man:ls as a directory, this makes no sense (#154173) if (!title.isEmpty() && title != "/") { error(KIO::ERR_IS_FILE, url.url()); return; } UDSEntryList uds_entry_list; if (section.isEmpty()) { for (QStringList::ConstIterator it = section_names.constBegin(); it != section_names.constEnd(); ++it) { UDSEntry uds_entry; QString name = "man:/(" + *it + ')'; uds_entry.insert( KIO::UDSEntry::UDS_NAME, sectionName( *it ) ); uds_entry.insert( KIO::UDSEntry::UDS_URL, name ); uds_entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); uds_entry_list.append( uds_entry ); } } QStringList list = findPages( section, QString(), false ); QStringList::Iterator it = list.begin(); QStringList::Iterator end = list.end(); for ( ; it != end; ++it ) { stripExtension( &(*it) ); UDSEntry uds_entry; uds_entry.insert( KIO::UDSEntry::UDS_NAME, *it ); uds_entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); uds_entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("text/html")); uds_entry_list.append( uds_entry ); } listEntries( uds_entry_list ); finished(); } void MANProtocol::getProgramPath() { if (!mySgml2RoffPath.isEmpty()) return; mySgml2RoffPath = KGlobal::dirs()->findExe("sgml2roff"); if (!mySgml2RoffPath.isEmpty()) return; /* sgml2roff isn't found in PATH. Check some possible locations where it may be found. */ mySgml2RoffPath = KGlobal::dirs()->findExe("sgml2roff", QString(SGML2ROFF_DIRS)); if (!mySgml2RoffPath.isEmpty()) return; /* Cannot find sgml2roff program: */ 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 KDE.")); finished(); exit(); }