/****************************************************************************** * * * * Copyright (C) 1997-2015 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. * */ /* http://www.cubic.org/source/archive/fileform/txt/man/ has some nice introductions to groff and man pages. */ #include #include #include "message.h" #include "mangen.h" #include "config.h" #include "util.h" #include "doxygen.h" #include #include "docparser.h" #include "mandocvisitor.h" #include "language.h" static QCString getExtension() { /* * [.][nuber][rest] * in case of . missing, just ignore it * in case number missing, just place a 3 in front of it */ QCString ext = Config_getString(MAN_EXTENSION); if (ext.isEmpty()) { ext = "3"; } else { if (ext.at(0)=='.') { if (ext.length()==1) { ext = "3"; } else // strip . { ext = ext.mid(1); } } if (ext.at(0)<'0' || ext.at(0)>'9') { ext.prepend("3"); } } return ext; } static QCString getSubdir() { QCString dir = Config_getString(MAN_SUBDIR); if (dir.isEmpty()) { dir = "man" + getExtension(); } return dir; } ManGenerator::ManGenerator() : OutputGenerator() { dir=Config_getString(MAN_OUTPUT) + "/" + getSubdir(); firstCol=TRUE; paragraph=TRUE; col=0; upperCase=FALSE; insideTabbing=FALSE; inHeader=FALSE; } ManGenerator::~ManGenerator() { } //void ManGenerator::append(const OutputGenerator *g) //{ // QCString r=g->getContents(); // if (upperCase) // t << r.upper(); // else // t << r; // if (!r.isEmpty()) // firstCol = r.at(r.length()-1)=='\n'; // else // firstCol = ((ManGenerator *)g)->firstCol; // col+=((ManGenerator *)g)->col; // inHeader=((ManGenerator *)g)->inHeader; // paragraph=FALSE; //} void ManGenerator::init() { QCString &manOutput = Config_getString(MAN_OUTPUT); QDir d(manOutput); if (!d.exists() && !d.mkdir(manOutput)) { err("Could not create output directory %s\n",manOutput.data()); exit(1); } d.setPath(manOutput + "/" + getSubdir()); if (!d.exists() && !d.mkdir(manOutput + "/" + getSubdir())) { err("Could not create output directory %s/%s\n",manOutput.data(), getSubdir().data()); exit(1); } createSubDirs(d); } static QCString buildFileName(const char *name) { QCString fileName; if (name==0) return "noname"; const char *p=name; char c; while ((c=*p++)) { switch (c) { case ':': fileName+="_"; if (*p==':') p++; break; case '<': case '>': case '&': case '*': case '!': case '^': case '~': case '%': case '+': case '/': fileName+="_"; break; default: fileName+=c; } } QCString manExtension = "." + getExtension(); if (fileName.right(manExtension.length())!=manExtension) { fileName+=manExtension; } return fileName; } void ManGenerator::startFile(const char *,const char *manName,const char *) { startPlainFile( buildFileName( manName ) ); firstCol=TRUE; } void ManGenerator::endFile() { t << endl; endPlainFile(); } void ManGenerator::endTitleHead(const char *,const char *name) { t << ".TH \"" << name << "\" " << getExtension() << " \"" << dateToString(FALSE) << "\" \""; if (!Config_getString(PROJECT_NUMBER).isEmpty()) t << "Version " << Config_getString(PROJECT_NUMBER) << "\" \""; if (Config_getString(PROJECT_NAME).isEmpty()) t << "Doxygen"; else t << Config_getString(PROJECT_NAME); t << "\" \\\" -*- nroff -*-" << endl; t << ".ad l" << endl; t << ".nh" << endl; t << ".SH NAME" << endl; t << name; firstCol=FALSE; paragraph=TRUE; inHeader=TRUE; } void ManGenerator::newParagraph() { if (!paragraph) { if (!firstCol) t << endl; t << ".PP" << endl; firstCol=TRUE; } paragraph=TRUE; } void ManGenerator::startParagraph(const char *) { if (!paragraph) { if (!firstCol) t << endl; t << ".PP" << endl; firstCol=TRUE; } paragraph=TRUE; } void ManGenerator::endParagraph() { } void ManGenerator::writeString(const char *text) { docify(text); } void ManGenerator::startIndexItem(const char *,const char *) { } void ManGenerator::endIndexItem(const char *,const char *) { } void ManGenerator::writeStartAnnoItem(const char *,const char *, const char *,const char *) { } void ManGenerator::writeObjectLink(const char *,const char *, const char *, const char *name) { startBold(); docify(name); endBold(); } void ManGenerator::writeCodeLink(const char *,const char *, const char *, const char *name, const char *) { docify(name); } void ManGenerator::startHtmlLink(const char *) { } void ManGenerator::endHtmlLink() { } //void ManGenerator::writeMailLink(const char *url) //{ // docify(url); //} void ManGenerator::startGroupHeader(int) { if (!firstCol) t << endl; t << ".SH \""; upperCase=TRUE; firstCol=FALSE; } void ManGenerator::endGroupHeader(int) { t << "\"\n.PP " << endl; firstCol=TRUE; paragraph=TRUE; upperCase=FALSE; } void ManGenerator::startMemberHeader(const char *) { if (!firstCol) t << endl; t << ".SS \""; } void ManGenerator::endMemberHeader() { t << "\"\n"; firstCol=TRUE; paragraph=FALSE; } void ManGenerator::docify(const char *str) { if (str) { const char *p=str; char c=0; while ((c=*p++)) { switch(c) { case '-': t << "\\-"; break; // see bug747780 case '.': t << "\\&."; break; // see bug652277 case '\\': t << "\\\\"; col++; break; case '\n': t << "\n"; col=0; break; case '\"': c = '\''; // no break! default: t << c; col++; break; } } firstCol=(c=='\n'); //printf("%s",str);fflush(stdout); } paragraph=FALSE; } void ManGenerator::codify(const char *str) { //static char spaces[]=" "; if (str) { const char *p=str; char c; int spacesToNextTabStop; while (*p) { c=*p++; switch(c) { case '.': t << "\\&."; break; // see bug652277 case '\t': spacesToNextTabStop = Config_getInt(TAB_SIZE) - (col%Config_getInt(TAB_SIZE)); t << Doxygen::spaces.left(spacesToNextTabStop); col+=spacesToNextTabStop; break; case '\n': t << "\n"; firstCol=TRUE; col=0; break; case '\\': t << "\\"; col++; break; case '\"': // no break! default: p=writeUtf8Char(t,p-1); firstCol=FALSE; col++; break; } } //printf("%s",str);fflush(stdout); } paragraph=FALSE; } void ManGenerator::writeChar(char c) { firstCol=(c=='\n'); if (firstCol) col=0; else col++; switch (c) { case '\\': t << "\\\\"; break; case '\"': c = '\''; // no break! default: t << c; break; } //printf("%c",c);fflush(stdout); paragraph=FALSE; } void ManGenerator::startDescList(SectionTypes) { if (!firstCol) { t << endl << ".PP" << endl; firstCol=TRUE; paragraph=TRUE; col=0; } paragraph=FALSE; startBold(); } void ManGenerator::startTitle() { if (!firstCol) t << endl; t << ".SH \""; firstCol=FALSE; paragraph=FALSE; } void ManGenerator::endTitle() { t << "\""; } void ManGenerator::startItemListItem() { if (!firstCol) t << endl; t << ".TP" << endl; firstCol=TRUE; paragraph=FALSE; col=0; } void ManGenerator::endItemListItem() { } void ManGenerator::startCodeFragment() { newParagraph(); t << ".nf" << endl; firstCol=TRUE; paragraph=FALSE; } void ManGenerator::endCodeFragment() { if (!firstCol) t << endl; t << ".fi" << endl; firstCol=TRUE; paragraph=FALSE; col=0; } void ManGenerator::startMemberDoc(const char *,const char *,const char *,const char *,int,int,bool) { if (!firstCol) t << endl; t << ".SS \""; firstCol=FALSE; paragraph=FALSE; } void ManGenerator::startDoxyAnchor(const char *,const char *manName, const char *, const char *name, const char *) { // something to be done? if( !Config_getBool(MAN_LINKS) ) { return; // no } // the name of the link file is derived from the name of the anchor: // - truncate after an (optional) :: QCString baseName = name; int i=baseName.findRev("::"); if (i!=-1) baseName=baseName.right(baseName.length()-i-2); //printf("Converting man link '%s'->'%s'->'%s'\n", // name,baseName.data(),buildFileName(baseName).data()); // - remove dangerous characters and append suffix, then add dir prefix QCString fileName=dir+"/"+buildFileName( baseName ); QFile linkfile( fileName ); // - only create file if it doesn't exist already if ( !linkfile.open( IO_ReadOnly ) ) { if ( linkfile.open( IO_WriteOnly ) ) { FTextStream linkstream; linkstream.setDevice(&linkfile); //linkstream.setEncoding(QTextStream::UnicodeUTF8); linkstream << ".so " << getSubdir() << "/" << buildFileName( manName ) << endl; } } linkfile.close(); } void ManGenerator::endMemberDoc(bool) { t << "\"\n"; } void ManGenerator::startSubsection() { if (!firstCol) t << endl; t << ".SS \""; firstCol=FALSE; paragraph=FALSE; } void ManGenerator::endSubsection() { t << "\""; } void ManGenerator::startSubsubsection() { if (!firstCol) t << endl; t << "\n.SS \""; firstCol=FALSE; paragraph=FALSE; } void ManGenerator::endSubsubsection() { t << "\""; } void ManGenerator::writeSynopsis() { if (!firstCol) t << endl; t << ".SH SYNOPSIS\n.br\n.PP\n"; firstCol=TRUE; paragraph=FALSE; } void ManGenerator::startDescItem() { if (!firstCol) t << endl; t << ".IP \""; firstCol=FALSE; } //void ManGenerator::endDescTitle() //{ // endBold(); // paragraph=TRUE; //} void ManGenerator::startDescForItem() { if (!firstCol) t << endl; if (!paragraph) t << ".in -1c" << endl; t << ".in +1c" << endl; firstCol=TRUE; paragraph=FALSE; col=0; } void ManGenerator::endDescForItem() { } void ManGenerator::endDescItem() { t << "\" 1c" << endl; firstCol=TRUE; } void ManGenerator::startAnonTypeScope(int indentLevel) { if (indentLevel==0) { insideTabbing=TRUE; } } void ManGenerator::endAnonTypeScope(int indentLevel) { if (indentLevel==0) { insideTabbing=FALSE; } } void ManGenerator::startMemberItem(const char *,int,const char *) { if (firstCol && !insideTabbing) t << ".in +1c\n"; t << "\n.ti -1c\n.RI \""; firstCol=FALSE; } void ManGenerator::endMemberItem() { t << "\"\n.br"; } void ManGenerator::startMemberList() { if (!insideTabbing) { t << "\n.in +1c"; firstCol=FALSE; } } void ManGenerator::endMemberList() { if (!insideTabbing) { t << "\n.in -1c"; firstCol=FALSE; } } void ManGenerator::startMemberGroupHeader(bool) { t << "\n.PP\n.RI \"\\fB"; } void ManGenerator::endMemberGroupHeader() { t << "\\fP\"\n.br\n"; firstCol=TRUE; } void ManGenerator::startMemberGroupDocs() { } void ManGenerator::endMemberGroupDocs() { t << "\n.PP"; } void ManGenerator::startMemberGroup() { t << "\n.in +1c"; } void ManGenerator::endMemberGroup(bool) { t << "\n.in -1c"; firstCol=FALSE; } void ManGenerator::startSection(const char *,const char *,SectionInfo::SectionType type) { if( !inHeader ) { switch(type) { case SectionInfo::Page: startGroupHeader(FALSE); break; case SectionInfo::Section: startGroupHeader(FALSE); break; case SectionInfo::Subsection: startMemberHeader(0); break; case SectionInfo::Subsubsection: startMemberHeader(0); break; case SectionInfo::Paragraph: startMemberHeader(0); break; default: ASSERT(0); break; } } } void ManGenerator::endSection(const char *,SectionInfo::SectionType type) { if( !inHeader ) { switch(type) { case SectionInfo::Page: endGroupHeader(0); break; case SectionInfo::Section: endGroupHeader(0); break; case SectionInfo::Subsection: endMemberHeader(); break; case SectionInfo::Subsubsection: endMemberHeader(); break; case SectionInfo::Paragraph: endMemberHeader(); break; default: ASSERT(0); break; } } else { t << "\n"; firstCol=TRUE; paragraph=FALSE; inHeader=FALSE; } } void ManGenerator::startSimpleSect(SectionTypes,const char *, const char *,const char *title) { if (!firstCol) { t << endl << ".PP" << endl; firstCol=TRUE; paragraph=TRUE; col=0; } paragraph=FALSE; startBold(); docify(title); endBold(); paragraph=TRUE; } void ManGenerator::endSimpleSect() { } void ManGenerator::startParamList(ParamListTypes,const char *title) { if (!firstCol) { t << endl << ".PP" << endl; firstCol=TRUE; paragraph=TRUE; col=0; } paragraph=FALSE; startBold(); docify(title); endBold(); paragraph=TRUE; } void ManGenerator::endParamList() { } void ManGenerator::writeDoc(DocNode *n,Definition *ctx,MemberDef *) { ManDocVisitor *visitor = new ManDocVisitor(t,*this,ctx?ctx->getDefFileExtension():QCString("")); n->accept(visitor); delete visitor; firstCol=FALSE; paragraph = FALSE; } void ManGenerator::startConstraintList(const char *header) { if (!firstCol) { t << endl << ".PP" << endl; firstCol=TRUE; paragraph=TRUE; col=0; } paragraph=FALSE; startBold(); docify(header); endBold(); paragraph=TRUE; } void ManGenerator::startConstraintParam() { startItemListItem(); startEmphasis(); } void ManGenerator::endConstraintParam() { endEmphasis(); endItemListItem(); t << " : "; } void ManGenerator::startConstraintType() { startEmphasis(); } void ManGenerator::endConstraintType() { endEmphasis(); } void ManGenerator::startConstraintDocs() { } void ManGenerator::endConstraintDocs() { t << endl; firstCol=TRUE; } void ManGenerator::endConstraintList() { } void ManGenerator::startInlineHeader() { if (!firstCol) { t << endl << ".PP" << endl << ".in -1c" << endl; } t << ".RI \"\\fB"; } void ManGenerator::endInlineHeader() { t << "\\fP\"" << endl << ".in +1c" << endl; firstCol = FALSE; } void ManGenerator::startMemberDocSimple(bool isEnum) { if (!firstCol) { t << endl << ".PP" << endl; } t << "\\fB"; if (isEnum) { docify(theTranslator->trEnumerationValues()); } else { docify(theTranslator->trCompoundMembers()); } t << ":\\fP" << endl; t << ".RS 4" << endl; } void ManGenerator::endMemberDocSimple(bool) { if (!firstCol) t << endl; t << ".RE" << endl; t << ".PP" << endl; firstCol=TRUE; } void ManGenerator::startInlineMemberType() { } void ManGenerator::endInlineMemberType() { t << " "; } void ManGenerator::startInlineMemberName() { t << "\\fI"; } void ManGenerator::endInlineMemberName() { t << "\\fP "; } void ManGenerator::startInlineMemberDoc() { } void ManGenerator::endInlineMemberDoc() { if (!firstCol) t << endl; t << ".br" << endl; t << ".PP" << endl; firstCol=TRUE; } void ManGenerator::startLabels() { } void ManGenerator::writeLabel(const char *l,bool isLast) { t << "\\fC [" << l << "]\\fP"; if (!isLast) t << ", "; } void ManGenerator::endLabels() { } void ManGenerator::endHeaderSection() { }