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