1 /******************************************************************************
3 * Copyright (C) 1997-2022 by Dimitri van Heesch.
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
16 /* http://www.cubic.org/source/archive/fileform/txt/man/ has some
17 nice introductions to groff and man pages. */
27 #include "docparser.h"
28 #include "mandocvisitor.h"
34 #include "outputlist.h"
36 static QCString getExtension()
40 * in case of . missing, just ignore it
41 * in case number missing, just place a 3 in front of it
43 QCString ext = Config_getString(MAN_EXTENSION);
61 if (ext.at(0)<'0' || ext.at(0)>'9')
69 static QCString getSubdir()
71 QCString dir = Config_getString(MAN_SUBDIR);
74 dir = "man" + getExtension();
79 static QCString docifyToString(const QCString &str)
82 result.reserve(str.length());
85 const char *p=str.data();
91 case '-': result += "\\-"; break; // see bug747780
92 case '.': result += "\\&."; break; // see bug652277
93 case '\\': result += "\\\\"; break;
94 case '\n': result += "\n"; break;
95 case '\"': c = '\''; // no break!
96 default: result += c; break;
99 //printf("%s",str);fflush(stdout);
104 static QCString objectLinkToString(const QCString &text)
106 return "\\fB" + docifyToString(text) + "\\fP";
109 //-------------------------------------------------------------------------------
111 ManCodeGenerator::ManCodeGenerator(TextStream *t) : m_t(t)
115 void ManCodeGenerator::startCodeFragment(const QCString &)
121 void ManCodeGenerator::endCodeFragment(const QCString &)
123 if (m_col>0) *m_t << "\n";
128 void ManCodeGenerator::writeLineNumber(const QCString &,const QCString &,const QCString &,int l, bool)
134 void ManCodeGenerator::writeCodeLink(CodeSymbolType,
135 const QCString &,const QCString &,
136 const QCString &, const QCString &name,
141 const char *p=name.data();
147 case '-': *m_t << "\\-"; break; // see bug747780
148 case '.': *m_t << "\\&."; break; // see bug652277
149 case '\\': *m_t << "\\\\"; m_col++; break;
150 case '\n': *m_t << "\n"; m_col=0; break;
151 case '\"': c = '\''; // no break!
152 default: *m_t << c; m_col++; break;
155 //printf("%s",str);fflush(stdout);
159 void ManCodeGenerator::codify(const QCString &str)
161 //static char spaces[]=" ";
164 const char *p=str.data();
166 int spacesToNextTabStop;
172 case '-': *m_t << "\\-"; break; // see bug747780
173 case '.': *m_t << "\\&."; break; // see bug652277
174 case '\t': spacesToNextTabStop =
175 Config_getInt(TAB_SIZE) - (m_col%Config_getInt(TAB_SIZE));
176 *m_t << Doxygen::spaces.left(spacesToNextTabStop);
177 m_col+=spacesToNextTabStop;
179 case '\n': *m_t << "\n"; m_col=0; break;
180 case '\\': *m_t << "\\\\"; m_col++; break;
181 case '\"': // no break!
182 default: p=writeUTF8Char(*m_t,p-1); m_col++; break;
185 //printf("%s",str);fflush(stdout);
190 //-------------------------------------------------------------------------------
192 ManGenerator::ManGenerator()
193 : OutputGenerator(Config_getString(MAN_OUTPUT)+"/"+getSubdir())
194 , m_codeList(std::make_unique<OutputCodeList>())
196 m_codeGen = m_codeList->add<ManCodeGenerator>(&m_t);
199 ManGenerator::ManGenerator(const ManGenerator &og) : OutputGenerator(og.m_dir)
201 m_codeList = std::make_unique<OutputCodeList>(*og.m_codeList);
202 m_codeGen = m_codeList->get<ManCodeGenerator>();
203 m_codeGen->setTextStream(&m_t);
204 m_firstCol = og.m_firstCol;
206 m_paragraph = og.m_paragraph;
207 m_upperCase = og.m_upperCase;
208 m_insideTabbing = og.m_insideTabbing;
209 m_inHeader = og.m_inHeader;
212 ManGenerator &ManGenerator::operator=(const ManGenerator &og)
217 m_codeList = std::make_unique<OutputCodeList>(*og.m_codeList);
218 m_codeGen = m_codeList->get<ManCodeGenerator>();
219 m_codeGen->setTextStream(&m_t);
220 m_firstCol = og.m_firstCol;
222 m_paragraph = og.m_paragraph;
223 m_upperCase = og.m_upperCase;
224 m_insideTabbing = og.m_insideTabbing;
225 m_inHeader = og.m_inHeader;
230 ManGenerator::ManGenerator(ManGenerator &&og)
231 : OutputGenerator(std::move(og))
233 m_codeList = std::exchange(og.m_codeList,std::unique_ptr<OutputCodeList>());
234 m_codeGen = m_codeList->get<ManCodeGenerator>();
235 m_codeGen->setTextStream(&m_t);
236 m_firstCol = std::exchange(og.m_firstCol,true);
237 m_col = std::exchange(og.m_col,0);
238 m_paragraph = std::exchange(og.m_paragraph,true);
239 m_upperCase = std::exchange(og.m_upperCase,false);
240 m_insideTabbing = std::exchange(og.m_insideTabbing,false);
241 m_inHeader = std::exchange(og.m_inHeader,false);
244 ManGenerator::~ManGenerator()
248 void ManGenerator::addCodeGen(OutputCodeList &list)
250 list.add(OutputCodeList::OutputCodeVariant(ManCodeGeneratorDefer(m_codeGen)));
253 void ManGenerator::init()
255 QCString manOutput = Config_getString(MAN_OUTPUT);
257 Dir d(manOutput.str());
258 if (!d.exists() && !d.mkdir(manOutput.str()))
260 term("Could not create output directory %s\n",qPrint(manOutput));
262 std::string manDir = manOutput.str()+"/"+getSubdir().str();
263 if (!d.exists(manDir) && !d.mkdir(manDir))
265 term("Could not create output directory %s/%s\n",qPrint(manOutput), qPrint(getSubdir()));
270 void ManGenerator::cleanup()
272 QCString dname = Config_getString(MAN_OUTPUT);
277 static QCString buildFileName(const QCString &name)
280 if (name.isEmpty()) return "noname";
282 const char *p=name.data();
309 QCString manExtension = "." + getExtension();
310 if (fileName.right(manExtension.length())!=manExtension)
312 fileName+=manExtension;
318 void ManGenerator::startFile(const QCString &,const QCString &manName,const QCString &,int,int)
320 startPlainFile( buildFileName( manName ) );
324 void ManGenerator::endFile()
330 void ManGenerator::endTitleHead(const QCString &,const QCString &name)
332 m_t << ".TH \"" << name << "\" " << getExtension() << " \"";
333 switch (Config_getEnum(TIMESTAMP))
335 case TIMESTAMP_t::YES:
336 case TIMESTAMP_t::DATETIME:
337 m_t << dateToString(DateTimeType::DateTime) << "\" \"";
339 case TIMESTAMP_t::DATE:
340 m_t << dateToString(DateTimeType::Date) << "\" \"";
342 case TIMESTAMP_t::NO:
345 if (!Config_getString(PROJECT_NUMBER).isEmpty())
346 m_t << "Version " << Config_getString(PROJECT_NUMBER) << "\" \"";
347 if (Config_getString(PROJECT_NAME).isEmpty())
350 m_t << Config_getString(PROJECT_NAME);
351 m_t << "\" \\\" -*- nroff -*-\n";
361 void ManGenerator::newParagraph()
365 if (!m_firstCol) m_t << "\n";
372 void ManGenerator::startParagraph(const QCString &)
376 if (!m_firstCol) m_t << "\n";
383 void ManGenerator::endParagraph()
387 void ManGenerator::writeString(const QCString &text)
392 void ManGenerator::startIndexItem(const QCString &,const QCString &)
396 void ManGenerator::endIndexItem(const QCString &,const QCString &)
400 void ManGenerator::writeStartAnnoItem(const QCString &,const QCString &,
401 const QCString &,const QCString &)
405 void ManGenerator::writeObjectLink(const QCString &,const QCString &,
406 const QCString &, const QCString &name)
408 startBold(); docify(name); endBold();
411 void ManGenerator::startGroupHeader(int)
413 if (!m_firstCol) m_t << "\n";
419 void ManGenerator::endGroupHeader(int)
427 void ManGenerator::startMemberHeader(const QCString &,int)
429 if (!m_firstCol) m_t << "\n";
433 void ManGenerator::endMemberHeader()
440 void ManGenerator::docify(const QCString &str)
444 const char *p=str.data();
450 case '-': m_t << "\\-"; break; // see bug747780
451 case '.': m_t << "\\&."; break; // see bug652277
452 case '\\': m_t << "\\\\"; m_col++; break;
453 case '\n': m_t << "\n"; m_col=0; break;
454 case '\"': c = '\''; // no break!
455 default: m_t << c; m_col++; break;
458 m_firstCol=(c=='\n');
459 //printf("%s",str);fflush(stdout);
464 void ManGenerator::writeChar(char c)
466 m_firstCol=(c=='\n');
467 if (m_firstCol) m_col=0; else m_col++;
470 case '\\': m_t << "\\\\"; break;
471 case '\"': c = '\''; // no break!
472 default: m_t << c; break;
474 //printf("%c",c);fflush(stdout);
478 void ManGenerator::startTitle()
480 if (!m_firstCol) m_t << "\n";
486 void ManGenerator::endTitle()
491 void ManGenerator::startItemListItem()
493 if (!m_firstCol) m_t << "\n";
500 void ManGenerator::endItemListItem()
504 void ManGenerator::startMemberDoc(const QCString &,const QCString &,const QCString &,const QCString &,int,int,bool)
506 if (!m_firstCol) m_t << "\n";
512 void ManGenerator::startDoxyAnchor(const QCString &,const QCString &manName,
513 const QCString &, const QCString &name,
516 // something to be done?
517 if( !Config_getBool(MAN_LINKS) )
522 // the name of the link file is derived from the name of the anchor:
523 // - truncate after an (optional) ::
524 QCString baseName = name;
525 int i=baseName.findRev("::");
526 if (i!=-1) baseName=baseName.right(baseName.length()-i-2);
528 //printf("Converting man link '%s'->'%s'->'%s'\n",
529 // name,qPrint(baseName),qPrint(buildFileName(baseName)));
531 // - remove dangerous characters and append suffix, then add dir prefix
532 QCString fileName=dir()+"/"+buildFileName( baseName );
533 FileInfo fi(fileName.str());
536 std::ofstream linkStream = Portable::openOutputStream(fileName);
537 if (linkStream.is_open())
539 linkStream << ".so " << getSubdir() << "/" << buildFileName( manName ) << "\n";
544 void ManGenerator::endMemberDoc(bool)
549 void ManGenerator::startCompoundTemplateParams()
551 if (!m_firstCol) m_t << "\n";
557 void ManGenerator::endCompoundTemplateParams()
561 void ManGenerator::writeSynopsis()
563 if (!m_firstCol) m_t << "\n";
564 m_t << ".SH SYNOPSIS\n.br\n.PP\n";
569 void ManGenerator::startDescForItem()
571 if (!m_firstCol) m_t << "\n";
572 if (!m_paragraph) m_t << ".in -1c\n";
579 void ManGenerator::endDescForItem()
583 void ManGenerator::startAnonTypeScope(int indentLevel)
587 m_insideTabbing=TRUE;
591 void ManGenerator::endAnonTypeScope(int indentLevel)
595 m_insideTabbing=FALSE;
600 void ManGenerator::startMemberItem(const QCString &,MemberItemType,const QCString &)
602 if (m_firstCol && !m_insideTabbing) m_t << ".in +1c\n";
603 m_t << "\n.ti -1c\n.RI \"";
607 void ManGenerator::endMemberItem(MemberItemType)
612 void ManGenerator::startMemberList()
614 if (!m_insideTabbing)
616 m_t << "\n.in +1c"; m_firstCol=FALSE;
620 void ManGenerator::endMemberList()
622 if (!m_insideTabbing)
624 m_t << "\n.in -1c"; m_firstCol=FALSE;
628 void ManGenerator::startMemberGroupHeader(bool)
630 m_t << "\n.PP\n.RI \"\\fB";
633 void ManGenerator::endMemberGroupHeader()
635 m_t << "\\fP\"\n.br\n";
639 void ManGenerator::startMemberGroupDocs()
643 void ManGenerator::endMemberGroupDocs()
648 void ManGenerator::startMemberGroup()
653 void ManGenerator::endMemberGroup(bool)
659 void ManGenerator::startSection(const QCString &,const QCString &,SectionType type)
665 case SectionType::Page: startGroupHeader(0); break;
666 case SectionType::Section: startGroupHeader(0); break;
667 case SectionType::Subsection: startMemberHeader(QCString(), -1); break;
668 case SectionType::Subsubsection: startMemberHeader(QCString(), -1); break;
669 case SectionType::Paragraph: startMemberHeader(QCString(), -1); break;
670 default: ASSERT(0); break;
675 void ManGenerator::endSection(const QCString &,SectionType type)
681 case SectionType::Page: endGroupHeader(0); break;
682 case SectionType::Section: endGroupHeader(0); break;
683 case SectionType::Subsection: endMemberHeader(); break;
684 case SectionType::Subsubsection: endMemberHeader(); break;
685 case SectionType::Paragraph: endMemberHeader(); break;
686 default: ASSERT(0); break;
698 void ManGenerator::startExamples()
701 { m_t << "\n" << ".PP\n";
702 m_firstCol=TRUE; m_paragraph=TRUE;
707 docify(theTranslator->trExamples());
712 void ManGenerator::endExamples()
716 void ManGenerator::startDescTable(const QCString &title)
720 m_firstCol=TRUE; m_paragraph=TRUE;
731 void ManGenerator::endDescTable()
736 void ManGenerator::writeDoc(const IDocNodeAST *ast,const Definition *ctx,const MemberDef *,int)
738 const DocNodeAST *astImpl = dynamic_cast<const DocNodeAST *>(ast);
741 ManDocVisitor visitor(m_t,*m_codeList,ctx?ctx->getDefFileExtension():QCString(""));
742 std::visit(visitor,astImpl->root);
748 void ManGenerator::startConstraintList(const QCString &header)
752 m_firstCol=TRUE; m_paragraph=TRUE;
762 void ManGenerator::startConstraintParam()
768 void ManGenerator::endConstraintParam()
775 void ManGenerator::startConstraintType()
780 void ManGenerator::endConstraintType()
785 void ManGenerator::startConstraintDocs()
789 void ManGenerator::endConstraintDocs()
791 m_t << "\n"; m_firstCol=TRUE;
794 void ManGenerator::endConstraintList()
799 void ManGenerator::startInlineHeader()
803 m_t << "\n.PP\n" << ".in -1c\n";
808 void ManGenerator::endInlineHeader()
810 m_t << "\\fP\"\n" << ".in +1c\n";
814 void ManGenerator::startMemberDocSimple(bool isEnum)
823 docify(theTranslator->trEnumerationValues());
827 docify(theTranslator->trCompoundMembers());
833 void ManGenerator::endMemberDocSimple(bool)
835 if (!m_firstCol) m_t << "\n";
841 void ManGenerator::startInlineMemberType()
845 void ManGenerator::endInlineMemberType()
850 void ManGenerator::startInlineMemberName()
855 void ManGenerator::endInlineMemberName()
860 void ManGenerator::startInlineMemberDoc()
864 void ManGenerator::endInlineMemberDoc()
866 if (!m_firstCol) m_t << "\n";
872 void ManGenerator::startLabels()
876 void ManGenerator::writeLabel(const QCString &l,bool isLast)
878 m_t << "\\fC [" << l << "]\\fP";
879 if (!isLast) m_t << ", ";
882 void ManGenerator::endLabels()
886 void ManGenerator::endHeaderSection()
890 void ManGenerator::writeInheritedSectionTitle(
891 const QCString &/*id*/, const QCString &/*ref*/,
892 const QCString &/*file*/, const QCString &/*anchor*/,
893 const QCString &title, const QCString &name)
896 m_t << theTranslator->trInheritedFrom(docifyToString(title), objectLinkToString(name));