17e6003f94767fefb7af8ef1722c999c66ea02ee
[platform/upstream/doxygen.git] / src / mangen.cpp
1 /******************************************************************************
2  *
3  * 
4  *
5  * Copyright (C) 1997-2015 by Dimitri van Heesch.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation under the terms of the GNU General Public License is hereby 
9  * granted. No representations are made about the suitability of this software 
10  * for any purpose. It is provided "as is" without express or implied warranty.
11  * See the GNU General Public License for more details.
12  *
13  * Documents produced by Doxygen are derivative works derived from the
14  * input used in their production; they are not affected by this license.
15  *
16  */
17
18 /* http://www.cubic.org/source/archive/fileform/txt/man/ has some
19    nice introductions to groff and man pages. */
20
21 #include <stdlib.h>
22
23 #include <qdir.h>
24 #include "message.h"
25 #include "mangen.h"
26 #include "config.h"
27 #include "util.h"
28 #include "doxygen.h"
29 #include <string.h>
30 #include "docparser.h"
31 #include "mandocvisitor.h"
32 #include "language.h"
33
34 static QCString getExtension()
35 {
36   /*
37    * [.][nuber][rest]
38    * in case of . missing, just ignore it
39    * in case number missing, just place a 3 in front of it
40    */
41   QCString ext = Config_getString(MAN_EXTENSION);
42   if (ext.isEmpty())
43   {
44     ext = "3"; 
45   }
46   else
47   {
48     if (ext.at(0)=='.')
49     {
50       if (ext.length()==1)
51       {
52         ext = "3"; 
53       }
54       else // strip .
55       {
56         ext = ext.mid(1);
57       }
58     }
59     if (ext.at(0)<'0' || ext.at(0)>'9')
60     {
61       ext.prepend("3");
62     }
63   }
64   return ext;
65 }
66
67 static QCString getSubdir()
68 {
69   QCString dir = Config_getString(MAN_SUBDIR);
70   if (dir.isEmpty())
71   {
72     dir = "man" + getExtension();
73   }
74   return dir;
75 }
76
77 ManGenerator::ManGenerator() : OutputGenerator()
78 {
79   dir=Config_getString(MAN_OUTPUT) + "/" + getSubdir();
80   firstCol=TRUE;
81   paragraph=TRUE;
82   col=0;
83   upperCase=FALSE;
84   insideTabbing=FALSE;
85   inHeader=FALSE;
86 }
87
88 ManGenerator::~ManGenerator()
89 {
90 }
91
92 //void ManGenerator::append(const OutputGenerator *g)
93 //{
94 //  QCString r=g->getContents();
95 //  if (upperCase)
96 //    t << r.upper();
97 //  else
98 //    t << r;
99 //  if (!r.isEmpty())
100 //    firstCol = r.at(r.length()-1)=='\n';
101 //  else
102 //    firstCol = ((ManGenerator *)g)->firstCol;
103 //  col+=((ManGenerator *)g)->col;
104 //  inHeader=((ManGenerator *)g)->inHeader;
105 //  paragraph=FALSE;
106 //}
107
108 void ManGenerator::init()
109 {
110   QCString &manOutput = Config_getString(MAN_OUTPUT);
111   
112   QDir d(manOutput);
113   if (!d.exists() && !d.mkdir(manOutput))
114   {
115     err("Could not create output directory %s\n",manOutput.data());
116     exit(1);
117   }
118   d.setPath(manOutput + "/" + getSubdir());
119   if (!d.exists() && !d.mkdir(manOutput + "/" + getSubdir()))
120   {
121     err("Could not create output directory %s/%s\n",manOutput.data(), getSubdir().data());
122     exit(1);
123   }
124   createSubDirs(d);
125 }
126
127 static QCString buildFileName(const char *name)
128 {
129   QCString fileName;
130   if (name==0) return "noname";
131
132   const char *p=name;
133   char c;
134   while ((c=*p++))
135   {
136     switch (c)
137     {
138       case ':':
139         fileName+="_";
140         if (*p==':') p++;
141         break;
142       case '<':
143       case '>':
144       case '&':
145       case '*':
146       case '!':
147       case '^':
148       case '~':
149       case '%':
150       case '+':
151       case '/':
152         fileName+="_";
153         break;
154       default:
155         fileName+=c;
156     }
157   }
158
159   QCString manExtension = "." + getExtension();
160   if (fileName.right(manExtension.length())!=manExtension) 
161   {
162     fileName+=manExtension;
163   }
164
165   return fileName;
166 }
167
168 void ManGenerator::startFile(const char *,const char *manName,const char *)
169 {
170   startPlainFile( buildFileName( manName ) );
171   firstCol=TRUE;
172 }
173
174 void ManGenerator::endFile()
175 {
176   t << endl;
177   endPlainFile();
178 }
179
180 void ManGenerator::endTitleHead(const char *,const char *name)
181 {
182   t << ".TH \"" << name << "\" " << getExtension() << " \"" 
183     << dateToString(FALSE) << "\" \"";
184   if (!Config_getString(PROJECT_NUMBER).isEmpty())
185     t << "Version " << Config_getString(PROJECT_NUMBER) << "\" \"";
186   if (Config_getString(PROJECT_NAME).isEmpty()) 
187     t << "Doxygen";
188   else
189     t << Config_getString(PROJECT_NAME);
190   t << "\" \\\" -*- nroff -*-" << endl;
191   t << ".ad l" << endl;
192   t << ".nh" << endl;
193   t << ".SH NAME" << endl;
194   t << name;
195   firstCol=FALSE;
196   paragraph=TRUE;
197   inHeader=TRUE;
198 }
199
200 void ManGenerator::newParagraph()
201 {
202   if (!paragraph)
203   {
204     if (!firstCol) t << endl;
205     t << ".PP" << endl;
206     firstCol=TRUE;
207   }
208   paragraph=TRUE;
209 }
210
211 void ManGenerator::startParagraph(const char *)
212 {
213   if (!paragraph)
214   {
215     if (!firstCol) t << endl;
216     t << ".PP" << endl;
217     firstCol=TRUE;
218   }
219   paragraph=TRUE;
220 }
221
222 void ManGenerator::endParagraph()
223 {
224 }
225
226 void ManGenerator::writeString(const char *text)
227 {
228   docify(text);
229 }
230
231 void ManGenerator::startIndexItem(const char *,const char *)
232 {
233 }
234
235 void ManGenerator::endIndexItem(const char *,const char *)
236 {
237 }
238
239 void ManGenerator::writeStartAnnoItem(const char *,const char *,
240                                        const char *,const char *)
241 {
242 }
243
244 void ManGenerator::writeObjectLink(const char *,const char *,
245                                     const char *, const char *name)
246 {
247   startBold(); docify(name); endBold();
248 }
249
250 void ManGenerator::writeCodeLink(const char *,const char *,
251                                  const char *, const char *name,
252                                  const char *)
253 {
254   docify(name);
255 }
256
257 void ManGenerator::startHtmlLink(const char *)
258 {
259 }
260
261 void ManGenerator::endHtmlLink()
262 {
263 }
264
265 //void ManGenerator::writeMailLink(const char *url)
266 //{
267 //  docify(url);
268 //}
269
270 void ManGenerator::startGroupHeader(int)
271 {
272   if (!firstCol) t << endl;
273   t << ".SH \"";
274   upperCase=TRUE;
275   firstCol=FALSE;
276 }
277
278 void ManGenerator::endGroupHeader(int)
279 {
280   t << "\"\n.PP " << endl;
281   firstCol=TRUE;
282   paragraph=TRUE;
283   upperCase=FALSE;
284 }
285
286 void ManGenerator::startMemberHeader(const char *)
287 {
288   if (!firstCol) t << endl;
289   t << ".SS \"";
290 }
291
292 void ManGenerator::endMemberHeader()
293 {
294   t << "\"\n";
295   firstCol=TRUE;
296   paragraph=FALSE;
297 }
298
299 void ManGenerator::docify(const char *str)
300 {
301   if (str)
302   {
303     const char *p=str;
304     char c=0;
305     while ((c=*p++)) 
306     {
307       switch(c)
308       {
309         case '-':  t << "\\-"; break; // see  bug747780
310         case '.':  t << "\\&."; break; // see  bug652277
311         case '\\': t << "\\\\"; col++; break;
312         case '\n': t << "\n"; col=0; break;
313         case '\"':  c = '\''; // no break!
314         default: t << c; col++; break;
315       }
316     }
317     firstCol=(c=='\n');
318     //printf("%s",str);fflush(stdout);
319   }
320   paragraph=FALSE;
321 }
322
323 void ManGenerator::codify(const char *str)
324 {
325   //static char spaces[]="        ";
326   if (str)
327   {
328     const char *p=str;
329     char c;
330     int spacesToNextTabStop;
331     while (*p)
332     {
333       c=*p++;
334       switch(c)
335       {
336         case '.':   t << "\\&."; break; // see  bug652277
337         case '\t':  spacesToNextTabStop =
338                           Config_getInt(TAB_SIZE) - (col%Config_getInt(TAB_SIZE));
339                     t << Doxygen::spaces.left(spacesToNextTabStop); 
340                     col+=spacesToNextTabStop; 
341                     break;
342         case '\n':  t << "\n"; firstCol=TRUE; col=0; break;
343         case '\\':  t << "\\"; col++; break;
344         case '\"':  // no break!
345         default:    p=writeUtf8Char(t,p-1); firstCol=FALSE; col++; break;
346       }
347     }
348     //printf("%s",str);fflush(stdout);
349   }
350   paragraph=FALSE;
351 }
352
353 void ManGenerator::writeChar(char c)
354 {
355   firstCol=(c=='\n');
356   if (firstCol) col=0; else col++;
357   switch (c)
358   {
359     case '\\': t << "\\\\"; break;
360   case '\"': c = '\''; // no break!
361     default:   t << c; break;
362   }
363   //printf("%c",c);fflush(stdout);
364   paragraph=FALSE;
365 }
366
367 void ManGenerator::startDescList(SectionTypes)      
368 {
369   if (!firstCol) 
370   { t << endl << ".PP" << endl; 
371     firstCol=TRUE; paragraph=TRUE; 
372     col=0;
373   }
374   paragraph=FALSE;
375   startBold();
376 }
377
378 void ManGenerator::startTitle() 
379
380   if (!firstCol) t << endl; 
381   t << ".SH \""; 
382   firstCol=FALSE;
383   paragraph=FALSE;
384 }
385
386 void ManGenerator::endTitle()
387 {
388     t << "\"";
389 }
390
391 void ManGenerator::startItemListItem() 
392
393   if (!firstCol) t << endl; 
394   t << ".TP" << endl; 
395   firstCol=TRUE;
396   paragraph=FALSE;
397   col=0;
398
399
400 void ManGenerator::endItemListItem()
401 {
402 }
403
404 void ManGenerator::startCodeFragment() 
405
406   newParagraph();
407   t << ".nf" << endl; 
408   firstCol=TRUE;
409   paragraph=FALSE;
410 }
411
412 void ManGenerator::endCodeFragment()   
413
414   if (!firstCol) t << endl;
415   t << ".fi" << endl; 
416   firstCol=TRUE;
417   paragraph=FALSE;
418   col=0;
419 }
420
421 void ManGenerator::startMemberDoc(const char *,const char *,const char *,const char *,int,int,bool) 
422
423   if (!firstCol) t << endl;
424   t << ".SS \""; 
425   firstCol=FALSE;
426   paragraph=FALSE;
427 }
428
429 void ManGenerator::startDoxyAnchor(const char *,const char *manName,
430                                    const char *, const char *name,
431                                    const char *)
432 {
433     // something to be done?
434     if( !Config_getBool(MAN_LINKS) ) 
435     {
436         return; // no
437     }
438
439     // the name of the link file is derived from the name of the anchor:
440     // - truncate after an (optional) ::
441     QCString baseName = name;
442     int i=baseName.findRev("::");
443     if (i!=-1) baseName=baseName.right(baseName.length()-i-2);
444
445     //printf("Converting man link '%s'->'%s'->'%s'\n",
446     //       name,baseName.data(),buildFileName(baseName).data());
447     
448     // - remove dangerous characters and append suffix, then add dir prefix
449     QCString fileName=dir+"/"+buildFileName( baseName );
450     QFile linkfile( fileName );
451     // - only create file if it doesn't exist already
452     if ( !linkfile.open( IO_ReadOnly ) ) 
453     {
454         if ( linkfile.open( IO_WriteOnly ) ) 
455         {
456               FTextStream linkstream;
457               linkstream.setDevice(&linkfile);
458               //linkstream.setEncoding(QTextStream::UnicodeUTF8);
459               linkstream << ".so " << getSubdir() << "/" << buildFileName( manName ) << endl;
460         }
461     }
462     linkfile.close();
463 }
464
465 void ManGenerator::endMemberDoc(bool)
466 {
467     t << "\"\n";
468 }
469
470 void ManGenerator::startSubsection()    
471
472   if (!firstCol) t << endl;
473   t << ".SS \""; 
474   firstCol=FALSE;
475   paragraph=FALSE;
476 }
477
478 void ManGenerator::endSubsection()
479 {
480   t << "\"";
481 }
482
483
484 void ManGenerator::startSubsubsection() 
485
486   if (!firstCol) t << endl;
487   t << "\n.SS \""; 
488   firstCol=FALSE;
489   paragraph=FALSE;
490 }
491
492 void ManGenerator::endSubsubsection()
493 {
494   t << "\"";
495 }
496
497 void ManGenerator::writeSynopsis()      
498
499   if (!firstCol) t << endl;
500   t << ".SH SYNOPSIS\n.br\n.PP\n"; 
501   firstCol=TRUE;
502   paragraph=FALSE;
503 }
504
505 void ManGenerator::startDescItem()
506 {
507   if (!firstCol) t << endl;
508   t << ".IP \"";
509   firstCol=FALSE;
510 }
511
512 //void ManGenerator::endDescTitle()
513 //{
514 //  endBold();
515 //  paragraph=TRUE;
516 //}
517
518 void ManGenerator::startDescForItem()
519 {
520   if (!firstCol) t << endl;
521   if (!paragraph) t << ".in -1c" << endl;
522   t << ".in +1c" << endl;
523   firstCol=TRUE;
524   paragraph=FALSE;
525   col=0;
526 }
527
528 void ManGenerator::endDescForItem()
529 {
530 }
531
532 void ManGenerator::endDescItem()
533 {
534   t << "\" 1c" << endl;
535   firstCol=TRUE;
536 }
537
538 void ManGenerator::startAnonTypeScope(int indentLevel)
539 {
540   if (indentLevel==0)
541   {
542     insideTabbing=TRUE;
543   }
544 }
545
546 void ManGenerator::endAnonTypeScope(int indentLevel)
547 {
548   if (indentLevel==0)
549   {
550     insideTabbing=FALSE;
551   }
552 }
553
554
555 void ManGenerator::startMemberItem(const char *,int,const char *) 
556
557   if (firstCol && !insideTabbing) t << ".in +1c\n";
558   t << "\n.ti -1c\n.RI \""; 
559   firstCol=FALSE;
560 }
561
562 void ManGenerator::endMemberItem() 
563
564   t << "\"\n.br"; 
565 }
566
567 void ManGenerator::startMemberList() 
568
569   if (!insideTabbing)
570   {
571     t << "\n.in +1c"; firstCol=FALSE; 
572   }
573 }
574
575 void ManGenerator::endMemberList() 
576
577   if (!insideTabbing)
578   {
579     t << "\n.in -1c"; firstCol=FALSE; 
580   }
581 }
582
583 void ManGenerator::startMemberGroupHeader(bool)
584 {
585   t << "\n.PP\n.RI \"\\fB";
586 }
587
588 void ManGenerator::endMemberGroupHeader()
589 {
590   t << "\\fP\"\n.br\n";
591   firstCol=TRUE;
592 }
593
594 void ManGenerator::startMemberGroupDocs()
595 {
596 }
597
598 void ManGenerator::endMemberGroupDocs()
599 {
600   t << "\n.PP";
601 }
602
603 void ManGenerator::startMemberGroup()
604 {
605   t << "\n.in +1c";
606 }
607
608 void ManGenerator::endMemberGroup(bool)
609 {
610   t << "\n.in -1c";
611   firstCol=FALSE;
612 }
613
614 void ManGenerator::startSection(const char *,const char *,SectionInfo::SectionType type)
615 {
616   if( !inHeader ) 
617   {
618     switch(type)
619     {
620       case SectionInfo::Page:          startGroupHeader(FALSE); break;
621       case SectionInfo::Section:       startGroupHeader(FALSE); break;
622       case SectionInfo::Subsection:    startMemberHeader(0); break;
623       case SectionInfo::Subsubsection: startMemberHeader(0); break;
624       case SectionInfo::Paragraph:     startMemberHeader(0); break;
625       default: ASSERT(0); break;
626     }
627   }
628 }
629
630 void ManGenerator::endSection(const char *,SectionInfo::SectionType type)
631 {
632   if( !inHeader )
633   {
634     switch(type)
635     {
636       case SectionInfo::Page:          endGroupHeader(0); break;
637       case SectionInfo::Section:       endGroupHeader(0); break;
638       case SectionInfo::Subsection:    endMemberHeader(); break;
639       case SectionInfo::Subsubsection: endMemberHeader(); break;
640       case SectionInfo::Paragraph:     endMemberHeader(); break;
641       default: ASSERT(0); break;
642     }
643   }
644   else
645   {
646     t << "\n";
647     firstCol=TRUE;
648     paragraph=FALSE;
649     inHeader=FALSE;
650   }
651 }
652
653 void ManGenerator::startSimpleSect(SectionTypes,const char *,
654                                    const char *,const char *title)
655 {
656   if (!firstCol) 
657   { t << endl << ".PP" << endl; 
658     firstCol=TRUE; paragraph=TRUE; 
659     col=0;
660   }
661   paragraph=FALSE;
662   startBold();
663   docify(title);
664   endBold();
665   paragraph=TRUE;
666 }
667
668 void ManGenerator::endSimpleSect()
669 {
670 }
671
672 void ManGenerator::startParamList(ParamListTypes,const char *title)
673 {
674   if (!firstCol) 
675   { t << endl << ".PP" << endl; 
676     firstCol=TRUE; paragraph=TRUE; 
677     col=0;
678   }
679   paragraph=FALSE;
680   startBold();
681   docify(title);
682   endBold();
683   paragraph=TRUE;
684 }
685
686 void ManGenerator::endParamList()
687 {
688 }
689
690 void ManGenerator::writeDoc(DocNode *n,Definition *ctx,MemberDef *)
691 {
692   ManDocVisitor *visitor = new ManDocVisitor(t,*this,ctx?ctx->getDefFileExtension():QCString(""));
693   n->accept(visitor);
694   delete visitor; 
695   firstCol=FALSE;
696   paragraph = FALSE;
697 }
698
699 void ManGenerator::startConstraintList(const char *header)
700 {
701   if (!firstCol) 
702   { t << endl << ".PP" << endl; 
703     firstCol=TRUE; paragraph=TRUE; 
704     col=0;
705   }
706   paragraph=FALSE;
707   startBold();
708   docify(header);
709   endBold();
710   paragraph=TRUE;
711 }
712
713 void ManGenerator::startConstraintParam()
714 {
715   startItemListItem();
716   startEmphasis();
717 }
718
719 void ManGenerator::endConstraintParam()
720 {
721   endEmphasis();
722   endItemListItem();
723   t << " : ";
724 }
725
726 void ManGenerator::startConstraintType()
727 {
728   startEmphasis();
729 }
730
731 void ManGenerator::endConstraintType()
732 {
733   endEmphasis();
734 }
735
736 void ManGenerator::startConstraintDocs()
737 {
738 }
739
740 void ManGenerator::endConstraintDocs()
741 {
742   t << endl; firstCol=TRUE;
743 }
744
745 void ManGenerator::endConstraintList()
746 {
747 }
748
749
750 void ManGenerator::startInlineHeader() 
751 {
752   if (!firstCol) 
753   {
754     t << endl << ".PP" << endl << ".in -1c" << endl;
755   }
756   t << ".RI \"\\fB"; 
757 }
758
759 void ManGenerator::endInlineHeader() 
760 {
761   t << "\\fP\"" << endl << ".in +1c" << endl;
762   firstCol = FALSE;
763 }
764
765 void ManGenerator::startMemberDocSimple(bool isEnum)
766 {
767   if (!firstCol) 
768   {
769     t << endl << ".PP" << endl;
770   }
771   t << "\\fB";
772   if (isEnum)
773   {
774     docify(theTranslator->trEnumerationValues());
775   }
776   else
777   {
778     docify(theTranslator->trCompoundMembers());
779   }
780   t << ":\\fP" << endl;
781   t << ".RS 4" << endl;
782 }
783
784 void ManGenerator::endMemberDocSimple(bool)
785 {
786   if (!firstCol) t << endl;
787   t << ".RE" << endl;
788   t << ".PP" << endl;
789   firstCol=TRUE;
790 }
791
792 void ManGenerator::startInlineMemberType()
793 {
794 }
795
796 void ManGenerator::endInlineMemberType()
797 {
798   t << " ";
799 }
800
801 void ManGenerator::startInlineMemberName()
802 {
803   t << "\\fI";
804 }
805
806 void ManGenerator::endInlineMemberName()
807 {
808   t << "\\fP ";
809 }
810
811 void ManGenerator::startInlineMemberDoc()
812 {
813 }
814
815 void ManGenerator::endInlineMemberDoc()
816 {
817   if (!firstCol) t << endl;
818   t << ".br" << endl;
819   t << ".PP" << endl;
820   firstCol=TRUE;
821 }
822
823 void ManGenerator::startLabels()
824 {
825 }
826
827 void ManGenerator::writeLabel(const char *l,bool isLast)
828 {
829   t << "\\fC [" << l << "]\\fP";
830   if (!isLast) t << ", ";
831 }
832
833 void ManGenerator::endLabels()
834 {
835 }
836
837 void ManGenerator::endHeaderSection()
838 {
839 }