Imported Upstream version 1.8.8
[platform/upstream/doxygen.git] / src / mangen.cpp
1 /******************************************************************************
2  *
3  * 
4  *
5  * Copyright (C) 1997-2014 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 ext = getExtension();
111   QCString &manOutput = Config_getString("MAN_OUTPUT");
112   
113   QDir d(manOutput);
114   if (!d.exists() && !d.mkdir(manOutput))
115   {
116     err("Could not create output directory %s\n",manOutput.data());
117     exit(1);
118   }
119   d.setPath(manOutput + "/" + getSubdir());
120   if (!d.exists() && !d.mkdir(manOutput + "/" + getSubdir()))
121   {
122     err("Could not create output directory %s/%s\n",manOutput.data(), getSubdir().data());
123     exit(1);
124   }
125   createSubDirs(d);
126 }
127
128 static QCString buildFileName(const char *name)
129 {
130   QCString fileName;
131   if (name==0) return "noname";
132
133   const char *p=name;
134   char c;
135   while ((c=*p++))
136   {
137     switch (c)
138     {
139       case ':':
140         fileName+="_";
141         if (*p==':') p++;
142         break;
143       case '<':
144       case '>':
145       case '&':
146       case '*':
147       case '!':
148       case '^':
149       case '~':
150       case '%':
151       case '+':
152       case '/':
153         fileName+="_";
154         break;
155       default:
156         fileName+=c;
157     }
158   }
159
160   QCString manExtension = "." + getExtension();
161   if (fileName.right(manExtension.length())!=manExtension) 
162   {
163     fileName+=manExtension;
164   }
165
166   return fileName;
167 }
168
169 void ManGenerator::startFile(const char *,const char *manName,const char *)
170 {
171   startPlainFile( buildFileName( manName ) );
172   firstCol=TRUE;
173 }
174
175 void ManGenerator::endFile()
176 {
177   t << endl;
178   endPlainFile();
179 }
180
181 void ManGenerator::endTitleHead(const char *,const char *name)
182 {
183   t << ".TH \"" << name << "\" " << getExtension() << " \"" 
184     << dateToString(FALSE) << "\" \"";
185   if (!Config_getString("PROJECT_NUMBER").isEmpty())
186     t << "Version " << Config_getString("PROJECT_NUMBER") << "\" \"";
187   if (Config_getString("PROJECT_NAME").isEmpty()) 
188     t << "Doxygen";
189   else
190     t << Config_getString("PROJECT_NAME");
191   t << "\" \\\" -*- nroff -*-" << endl;
192   t << ".ad l" << endl;
193   t << ".nh" << endl;
194   t << ".SH NAME" << endl;
195   t << name << " \\- ";
196   firstCol=FALSE;
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()
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  bug652277
310         case '\\': t << "\\\\"; col++; break;
311         case '\n': t << "\n"; col=0; break;
312         case '\"':  c = '\''; // no break!
313         default: t << c; col++; break;
314       }
315     }
316     firstCol=(c=='\n');
317     //printf("%s",str);fflush(stdout);
318   }
319   paragraph=FALSE;
320 }
321
322 void ManGenerator::codify(const char *str)
323 {
324   //static char spaces[]="        ";
325   if (str)
326   {
327     const char *p=str;
328     char c;
329     int spacesToNextTabStop;
330     while (*p)
331     {
332       c=*p++;
333       switch(c)
334       {
335         case '.':   t << "\\&."; break; // see  bug652277
336         case '\t':  spacesToNextTabStop =
337                           Config_getInt("TAB_SIZE") - (col%Config_getInt("TAB_SIZE"));
338                     t << Doxygen::spaces.left(spacesToNextTabStop); 
339                     col+=spacesToNextTabStop; 
340                     break;
341         case '\n':  t << "\n"; firstCol=TRUE; col=0; break;
342         case '\\':  t << "\\"; col++; break;
343         case '\"':  c = '\''; // no break!
344         default:    p=writeUtf8Char(t,p-1); firstCol=FALSE; col++; break;
345       }
346     }
347     //printf("%s",str);fflush(stdout);
348   }
349   paragraph=FALSE;
350 }
351
352 void ManGenerator::writeChar(char c)
353 {
354   firstCol=(c=='\n');
355   if (firstCol) col=0; else col++;
356   switch (c)
357   {
358     case '\\': t << "\\\\"; break;
359   case '\"': c = '\''; // no break!
360     default:   t << c; break;
361   }
362   //printf("%c",c);fflush(stdout);
363   paragraph=FALSE;
364 }
365
366 void ManGenerator::startDescList(SectionTypes)      
367 {
368   if (!firstCol) 
369   { t << endl << ".PP" << endl; 
370     firstCol=TRUE; paragraph=TRUE; 
371     col=0;
372   }
373   paragraph=FALSE;
374   startBold();
375 }
376
377 void ManGenerator::startTitle() 
378
379   if (!firstCol) t << endl; 
380   t << ".SH \""; 
381   firstCol=FALSE;
382   paragraph=FALSE;
383 }
384
385 void ManGenerator::endTitle()
386 {
387     t << "\"";
388 }
389
390 void ManGenerator::startItemListItem() 
391
392   if (!firstCol) t << endl; 
393   t << ".TP" << endl; 
394   firstCol=TRUE;
395   paragraph=FALSE;
396   col=0;
397
398
399 void ManGenerator::endItemListItem()
400 {
401 }
402
403 void ManGenerator::startCodeFragment() 
404
405   newParagraph();
406   t << ".nf" << endl; 
407   firstCol=TRUE;
408   paragraph=FALSE;
409 }
410
411 void ManGenerator::endCodeFragment()   
412
413   if (!firstCol) t << endl;
414   t << ".fi" << endl; 
415   firstCol=TRUE;
416   paragraph=FALSE;
417   col=0;
418 }
419
420 void ManGenerator::startMemberDoc(const char *,const char *,const char *,const char *,bool) 
421
422   if (!firstCol) t << endl;
423   t << ".SS \""; 
424   firstCol=FALSE;
425   paragraph=FALSE;
426 }
427
428 void ManGenerator::startDoxyAnchor(const char *,const char *manName,
429                                    const char *, const char *name,
430                                    const char *)
431 {
432     // something to be done?
433     if( !Config_getBool("MAN_LINKS") ) 
434     {
435         return; // no
436     }
437
438     // the name of the link file is derived from the name of the anchor:
439     // - truncate after an (optional) ::
440     QCString baseName = name;
441     int i=baseName.findRev("::");
442     if (i!=-1) baseName=baseName.right(baseName.length()-i-2);
443
444     //printf("Converting man link '%s'->'%s'->'%s'\n",
445     //       name,baseName.data(),buildFileName(baseName).data());
446     
447     // - remove dangerous characters and append suffix, then add dir prefix
448     QCString fileName=dir+"/"+buildFileName( baseName );
449     QFile linkfile( fileName );
450     // - only create file if it doesn't exist already
451     if ( !linkfile.open( IO_ReadOnly ) ) 
452     {
453         if ( linkfile.open( IO_WriteOnly ) ) 
454         {
455               FTextStream linkstream;
456               linkstream.setDevice(&linkfile);
457               //linkstream.setEncoding(QTextStream::UnicodeUTF8);
458               linkstream << ".so " << getSubdir() << "/" << buildFileName( manName ) << endl;
459         }
460     }
461     linkfile.close();
462 }
463
464 void ManGenerator::endMemberDoc(bool)
465 {
466     t << "\"\n";
467 }
468
469 void ManGenerator::startSubsection()    
470
471   if (!firstCol) t << endl;
472   t << ".SS \""; 
473   firstCol=FALSE;
474   paragraph=FALSE;
475 }
476
477 void ManGenerator::endSubsection()
478 {
479   t << "\"";
480 }
481
482
483 void ManGenerator::startSubsubsection() 
484
485   if (!firstCol) t << endl;
486   t << "\n.SS \""; 
487   firstCol=FALSE;
488   paragraph=FALSE;
489 }
490
491 void ManGenerator::endSubsubsection()
492 {
493   t << "\"";
494 }
495
496 void ManGenerator::writeSynopsis()      
497
498   if (!firstCol) t << endl;
499   t << ".SH SYNOPSIS\n.br\n.PP\n"; 
500   firstCol=TRUE;
501   paragraph=FALSE;
502 }
503
504 void ManGenerator::startDescItem()
505 {
506   if (!firstCol) t << endl;
507   t << ".IP \"";
508   firstCol=FALSE;
509 }
510
511 //void ManGenerator::endDescTitle()
512 //{
513 //  endBold();
514 //  paragraph=TRUE;
515 //}
516
517 void ManGenerator::startDescForItem()
518 {
519   if (!firstCol) t << endl;
520   if (!paragraph) t << ".in -1c" << endl;
521   t << ".in +1c" << endl;
522   firstCol=TRUE;
523   paragraph=FALSE;
524   col=0;
525 }
526
527 void ManGenerator::endDescForItem()
528 {
529 }
530
531 void ManGenerator::endDescItem()
532 {
533   t << "\" 1c" << endl;
534   firstCol=TRUE;
535 }
536
537 void ManGenerator::startAnonTypeScope(int indentLevel)
538 {
539   if (indentLevel==0)
540   {
541     insideTabbing=TRUE;
542   }
543 }
544
545 void ManGenerator::endAnonTypeScope(int indentLevel)
546 {
547   if (indentLevel==0)
548   {
549     insideTabbing=FALSE;
550   }
551 }
552
553
554 void ManGenerator::startMemberItem(const char *,int,const char *) 
555
556   if (firstCol && !insideTabbing) t << ".in +1c\n";
557   t << "\n.ti -1c\n.RI \""; 
558   firstCol=FALSE;
559 }
560
561 void ManGenerator::endMemberItem() 
562
563   t << "\"\n.br"; 
564 }
565
566 void ManGenerator::startMemberList() 
567
568   if (!insideTabbing)
569   {
570     t << "\n.in +1c"; firstCol=FALSE; 
571   }
572 }
573
574 void ManGenerator::endMemberList() 
575
576   if (!insideTabbing)
577   {
578     t << "\n.in -1c"; firstCol=FALSE; 
579   }
580 }
581
582 void ManGenerator::startMemberGroupHeader(bool)
583 {
584   t << "\n.PP\n.RI \"\\fB";
585 }
586
587 void ManGenerator::endMemberGroupHeader()
588 {
589   t << "\\fP\"\n.br\n";
590   firstCol=TRUE;
591 }
592
593 void ManGenerator::startMemberGroupDocs()
594 {
595 }
596
597 void ManGenerator::endMemberGroupDocs()
598 {
599   t << "\n.PP";
600 }
601
602 void ManGenerator::startMemberGroup()
603 {
604   t << "\n.in +1c";
605 }
606
607 void ManGenerator::endMemberGroup(bool)
608 {
609   t << "\n.in -1c";
610   firstCol=FALSE;
611 }
612
613 void ManGenerator::startSection(const char *,const char *,SectionInfo::SectionType type)
614 {
615   if( !inHeader ) 
616   {
617     switch(type)
618     {
619       case SectionInfo::Page:          startGroupHeader(FALSE); break;
620       case SectionInfo::Section:       startGroupHeader(FALSE); break;
621       case SectionInfo::Subsection:    startMemberHeader(0); break;
622       case SectionInfo::Subsubsection: startMemberHeader(0); break;
623       case SectionInfo::Paragraph:     startMemberHeader(0); break;
624       default: ASSERT(0); break;
625     }
626   }
627 }
628
629 void ManGenerator::endSection(const char *,SectionInfo::SectionType type)
630 {
631   if( !inHeader )
632   {
633     switch(type)
634     {
635       case SectionInfo::Page:          endGroupHeader(0); break;
636       case SectionInfo::Section:       endGroupHeader(0); break;
637       case SectionInfo::Subsection:    endMemberHeader(); break;
638       case SectionInfo::Subsubsection: endMemberHeader(); break;
639       case SectionInfo::Paragraph:     endMemberHeader(); break;
640       default: ASSERT(0); break;
641     }
642   }
643   else
644   {
645     t << "\n";
646     firstCol=TRUE;
647     paragraph=FALSE;
648     inHeader=FALSE;
649   }
650 }
651
652 void ManGenerator::startSimpleSect(SectionTypes,const char *,
653                                    const char *,const char *title)
654 {
655   if (!firstCol) 
656   { t << endl << ".PP" << endl; 
657     firstCol=TRUE; paragraph=TRUE; 
658     col=0;
659   }
660   paragraph=FALSE;
661   startBold();
662   docify(title);
663   endBold();
664   paragraph=TRUE;
665 }
666
667 void ManGenerator::endSimpleSect()
668 {
669 }
670
671 void ManGenerator::startParamList(ParamListTypes,const char *title)
672 {
673   if (!firstCol) 
674   { t << endl << ".PP" << endl; 
675     firstCol=TRUE; paragraph=TRUE; 
676     col=0;
677   }
678   paragraph=FALSE;
679   startBold();
680   docify(title);
681   endBold();
682   paragraph=TRUE;
683 }
684
685 void ManGenerator::endParamList()
686 {
687 }
688
689 void ManGenerator::writeDoc(DocNode *n,Definition *ctx,MemberDef *)
690 {
691   ManDocVisitor *visitor = new ManDocVisitor(t,*this,ctx?ctx->getDefFileExtension():QCString(""));
692   n->accept(visitor);
693   delete visitor; 
694   firstCol=FALSE;
695   paragraph = FALSE;
696 }
697
698 void ManGenerator::startConstraintList(const char *header)
699 {
700   if (!firstCol) 
701   { t << endl << ".PP" << endl; 
702     firstCol=TRUE; paragraph=TRUE; 
703     col=0;
704   }
705   paragraph=FALSE;
706   startBold();
707   docify(header);
708   endBold();
709   paragraph=TRUE;
710 }
711
712 void ManGenerator::startConstraintParam()
713 {
714   startItemListItem();
715   startEmphasis();
716 }
717
718 void ManGenerator::endConstraintParam()
719 {
720   endEmphasis();
721   endItemListItem();
722   t << " : ";
723 }
724
725 void ManGenerator::startConstraintType()
726 {
727   startEmphasis();
728 }
729
730 void ManGenerator::endConstraintType()
731 {
732   endEmphasis();
733 }
734
735 void ManGenerator::startConstraintDocs()
736 {
737 }
738
739 void ManGenerator::endConstraintDocs()
740 {
741   t << endl; firstCol=TRUE;
742 }
743
744 void ManGenerator::endConstraintList()
745 {
746 }
747
748
749 void ManGenerator::startInlineHeader() 
750 {
751   if (!firstCol) 
752   {
753     t << endl << ".PP" << endl << ".in -1c" << endl;
754   }
755   t << ".RI \"\\fB"; 
756 }
757
758 void ManGenerator::endInlineHeader() 
759 {
760   t << "\\fP\"" << endl << ".in +1c" << endl;
761   firstCol = FALSE;
762 }
763
764 void ManGenerator::startMemberDocSimple()
765 {
766   if (!firstCol) 
767   {
768     t << endl << ".PP" << endl;
769   }
770   t << "\\fB";
771   docify(theTranslator->trCompoundMembers());
772   t << ":\\fP" << endl;
773   t << ".RS 4" << endl;
774 }
775
776 void ManGenerator::endMemberDocSimple()
777 {
778   if (!firstCol) t << endl;
779   t << ".RE" << endl;
780   t << ".PP" << endl;
781   firstCol=TRUE;
782 }
783
784 void ManGenerator::startInlineMemberType()
785 {
786 }
787
788 void ManGenerator::endInlineMemberType()
789 {
790   t << " ";
791 }
792
793 void ManGenerator::startInlineMemberName()
794 {
795   t << "\\fI";
796 }
797
798 void ManGenerator::endInlineMemberName()
799 {
800   t << "\\fP ";
801 }
802
803 void ManGenerator::startInlineMemberDoc()
804 {
805 }
806
807 void ManGenerator::endInlineMemberDoc()
808 {
809   if (!firstCol) t << endl;
810   t << ".br" << endl;
811   t << ".PP" << endl;
812   firstCol=TRUE;
813 }
814
815 void ManGenerator::startLabels()
816 {
817 }
818
819 void ManGenerator::writeLabel(const char *l,bool isLast)
820 {
821   t << "\\fC [" << l << "]\\fP";
822   if (!isLast) t << ", ";
823 }
824
825 void ManGenerator::endLabels()
826 {
827 }
828
829 void ManGenerator::endHeaderSection()
830 {
831   if (!firstCol) 
832   { 
833     t<< endl; firstCol=TRUE; 
834   }
835 }
836
837