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