1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "manpage.h"
4 
5 #include "class.h"
6 #include "function.h"
7 #include "file.h"
8 
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <sys/fcntl.h>
12 #include <unistd.h>
13 
14 
15 static ManPage * mp = 0;
16 
17 
18 /*! \class ManPage manpage.h
19   The ManPage class provides documentation output to a UNIX man page.
20 
21   It implements the same functions as Output, but they're not static,
22   and is called when Output's static functions are called.
23 */
24 
25 
26 /*! Constructs an empty man page generator which will write man pages
27     in the \a dir directory. */
28 
ManPage(const char * dir)29 ManPage::ManPage( const char * dir )
30     : para( false ), fd( -1 ), directory( dir )
31 {
32     mp = this;
33 }
34 
35 
36 /*! Destroys the man page object, flushing and closing the generated file. */
37 
~ManPage()38 ManPage::~ManPage()
39 {
40     endPage();
41     mp = 0;
42 }
43 
44 
45 /*! Returns a pointer to the most recently constructed ManPage object,
46     or a null pointer if none has been constructed yet. */
47 
current()48 ManPage * ManPage::current()
49 {
50     return mp;
51 }
52 
53 
54 /*! For the moment, we do not generate introductory manual
55     pages. Perhaps it would be possible. This function makes ManPage
56     discard output until startHeadline() is called for a Class.
57 */
58 
startHeadline(Intro *)59 void ManPage::startHeadline( Intro * )
60 {
61     endPage();
62 }
63 
64 
65 /*! As Output::startHeadline(). \a c is used only to generate a
66     suitable man page named.
67 */
68 
startHeadline(Class * c)69 void ManPage::startHeadline( Class * c )
70 {
71     endPage();
72     EString filename = directory + "/" + c->name().lower() + ".3oryx";
73     fd = ::open( filename.cstr(), O_CREAT|O_WRONLY|O_TRUNC, 0644 );
74     para = true;
75     output( ".\\\" generated by udoc from source code\n"
76             ".TH " );
77     addText( c->name() );
78     output( " 3oryx x/x/x Oryx Oryx\n"
79             ".nh\n"
80             ".SH NAME\n" );
81     addText( c->name() );
82     output( " class\n"
83             ".SH SYNOPSIS\n"
84             "\\fC#include <" );
85     addText( c->file()->name() );
86     output( ">\\fR\n"
87             ".SH DESCRIPTION\n" );
88 }
89 
90 
91 /*! As Output::startHeadline().
92 */
93 
startHeadline(Function *)94 void ManPage::startHeadline( Function * )
95 {
96     output( ".SH " );
97     para = true;
98 }
99 
100 
101 /*! As Output::endParagraph(). */
102 
endParagraph()103 void ManPage::endParagraph()
104 {
105     if ( para )
106         output( "\n" );
107     para = false;
108 }
109 
110 
111 /*! As Output::addText(). \a text is escaped (how?). */
112 
addText(const EString & text)113 void ManPage::addText( const EString & text )
114 {
115     if ( !para ) {
116         output( ".PP\n" );
117         para = true;
118     }
119 
120     EString s;
121     uint i = 0;
122     while ( i < text.length() ) {
123         if ( text[i] == '\\' )
124             s.append( "\\\\" );
125         else
126             s.append( text[i] );
127         i++;
128     }
129     output( s );
130 }
131 
132 
133 /*! As Output::addArgument(). \a text is used italicized. */
134 
addArgument(const EString & text)135 void ManPage::addArgument( const EString & text )
136 {
137     addText( "" );
138     output( "\\fI" );
139     addText( text );
140     output( "\\fR" );
141 }
142 
143 
144 /*! As Output::addFunction(). At present this outputs \a text in the
145     regular font, maybe it should use a different font?
146 
147     The class to which \a f belongs is mentioned in the "see also" section.
148 */
149 
addFunction(const EString & text,Function * f)150 void ManPage::addFunction( const EString & text, Function * f )
151 {
152     addText( text );
153     addClass( "", f->parent() ); // no text, but make a See Also
154 }
155 
156 
157 /*! As Output::addClass(). \a text is output as-is, and the name of \a
158     c is remembered for later mention in the See Also section.
159 */
160 
addClass(const EString & text,Class * c)161 void ManPage::addClass( const EString & text, Class * c )
162 {
163     addText( text );
164     EString n = c->name() + "(3oryx)";
165     if ( !references.find( n ) )
166         references.insert( new EString( n ) );
167 }
168 
169 
170 /*! Write \a s to the output file. */
171 
output(const EString & s)172 void ManPage::output( const EString & s )
173 {
174     if ( fd < 0 || s.isEmpty() )
175         return;
176     int r = ::write( fd, s.data(), s.length() );
177     r = r;
178 }
179 
180 
181 /*! Adds a See Also section mentioning everything we've mentioned
182     (using addClass()).
183 */
184 
addReferences()185 void ManPage::addReferences()
186 {
187     if ( references.isEmpty() )
188         return;
189     endParagraph();
190     output( ".SH SEE ALSO\n.ad l\n" );
191     SortedList<EString>::Iterator it( references );
192     while ( it ) {
193         EString s( *it );
194         ++it;
195         addText( s );
196         if ( !it )
197             addText( "." );
198         else if ( it == references.last() )
199             addText( " and " );
200         else
201             addText( ", " );
202     }
203     references.clear();
204 }
205 
206 
207 /*! Add boilerplate describing the author. Will need configurability. */
208 
addAuthor()209 void ManPage::addAuthor()
210 {
211     endParagraph();
212     output( ".SH AUTHOR\n" );
213     addText( "Automatically generated from source code belonging to " );
214     addText( Output::owner() );
215     if ( !Output::ownerHome().isEmpty() ) {
216         addText( " (" );
217         addText( Output::ownerHome() );
218         addText( ")" );
219     }
220     addText( ". All rights reserved." );
221     endParagraph();
222 }
223 
224 
225 /*! Emits the routing verbiage at the end of a manpage. */
226 
endPage()227 void ManPage::endPage()
228 {
229     if ( fd < 0 )
230         return;
231 
232     addAuthor();
233     addReferences();
234     endParagraph();
235     ::close( fd );
236 }
237