Fix for UBSan build
[platform/upstream/doxygen.git] / src / docsets.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2012 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby 
7  * granted. No representations are made about the suitability of this software 
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15
16 #include <qfile.h>
17 #include "docsets.h"
18 #include "config.h"
19 #include "message.h"
20 #include "doxygen.h"
21
22 DocSets::DocSets() : m_nodes(17), m_scopes(17)
23 {
24   m_nf = 0;
25   m_tf = 0;
26   m_dc = 0;
27   m_id = 0;
28   m_nodes.setAutoDelete(TRUE);
29 }
30
31 DocSets::~DocSets()
32 {
33   delete m_nf;
34   delete m_tf;
35 }
36
37 void DocSets::initialize()
38 {
39   // -- get config options
40   QCString projectName = Config_getString("PROJECT_NAME");
41   if (projectName.isEmpty()) projectName="root";
42   QCString bundleId = Config_getString("DOCSET_BUNDLE_ID");
43   if (bundleId.isEmpty()) bundleId="org.doxygen.Project";
44   QCString feedName = Config_getString("DOCSET_FEEDNAME");
45   if (feedName.isEmpty()) feedName="FeedName";
46   QCString publisherId = Config_getString("DOCSET_PUBLISHER_ID");
47   if (publisherId.isEmpty()) publisherId="PublisherId";
48   QCString publisherName = Config_getString("DOCSET_PUBLISHER_NAME");
49   if (publisherName.isEmpty()) publisherName="PublisherName";
50   QCString projectNumber = Config_getString("PROJECT_NUMBER");
51   if (projectNumber.isEmpty()) projectNumber="ProjectNumber";
52
53   // -- write Makefile
54   {
55   QCString mfName = Config_getString("HTML_OUTPUT") + "/Makefile";
56   QFile makefile(mfName);
57   if (!makefile.open(IO_WriteOnly))
58   {
59     err("Could not open file %s for writing\n",mfName.data());
60     exit(1);
61   }
62   FTextStream ts(&makefile);
63
64   ts << "DOCSET_NAME=" << bundleId << ".docset\n" 
65         "DOCSET_CONTENTS=$(DOCSET_NAME)/Contents\n"
66         "DOCSET_RESOURCES=$(DOCSET_CONTENTS)/Resources\n"
67         "DOCSET_DOCUMENTS=$(DOCSET_RESOURCES)/Documents\n"
68         "DESTDIR=~/Library/Developer/Shared/Documentation/DocSets\n"
69         "XCODE_INSTALL=$(shell xcode-select -print-path)\n"
70         "\n"
71         "all: docset\n"
72         "\n"
73         "docset:\n"
74         "\tmkdir -p $(DOCSET_DOCUMENTS)\n"
75         "\tcp Nodes.xml $(DOCSET_RESOURCES)\n"
76         "\tcp Tokens.xml $(DOCSET_RESOURCES)\n"
77         "\tcp Info.plist $(DOCSET_CONTENTS)\n"
78         "\ttar --exclude $(DOCSET_NAME) \\\n"
79         "\t    --exclude Nodes.xml \\\n"
80         "\t    --exclude Tokens.xml \\\n"
81         "\t    --exclude Info.plist \\\n"
82         "\t    --exclude Makefile -c -f - . \\\n"
83         "\t    | (cd $(DOCSET_DOCUMENTS); tar xvf -)\n"
84         "\t$(XCODE_INSTALL)/usr/bin/docsetutil index $(DOCSET_NAME)\n"
85         "\trm -f $(DOCSET_DOCUMENTS)/Nodes.xml\n"
86         "\trm -f $(DOCSET_DOCUMENTS)/Info.plist\n"
87         "\trm -f $(DOCSET_DOCUMENTS)/Makefile\n"
88         "\trm -f $(DOCSET_RESOURCES)/Nodes.xml\n"
89         "\trm -f $(DOCSET_RESOURCES)/Tokens.xml\n"
90         "\n"
91         "clean:\n"
92         "\trm -rf $(DOCSET_NAME)\n"
93         "\n"
94         "install: docset\n"
95         "\tmkdir -p $(DESTDIR)\n"
96         "\tcp -R $(DOCSET_NAME) $(DESTDIR)\n"
97         "\n"
98         "uninstall:\n"
99         "\trm -rf $(DESTDIR)/$(DOCSET_NAME)\n"
100         "\n"
101         "always:\n";
102   }
103
104   // -- write Info.plist
105   {
106   QCString plName = Config_getString("HTML_OUTPUT") + "/Info.plist";
107   QFile plist(plName);
108   if (!plist.open(IO_WriteOnly))
109   {
110     err("Could not open file %s for writing\n",plName.data());
111     exit(1);
112   }
113   FTextStream ts(&plist);
114
115   ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 
116         "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"\n" 
117         "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 
118         "<plist version=\"1.0\">\n" 
119         "<dict>\n" 
120         "     <key>CFBundleName</key>\n" 
121         "     <string>" << projectName << "</string>\n" 
122         "     <key>CFBundleIdentifier</key>\n"
123         "     <string>" << bundleId << "</string>\n" 
124         "     <key>CFBundleVersion</key>\n"
125         "     <string>" << projectNumber << "</string>\n"
126         "     <key>DocSetFeedName</key>\n" 
127         "     <string>" << feedName << "</string>\n"
128         "     <key>DocSetPublisherIdentifier</key>\n"
129         "     <string>" << publisherId << "</string>\n"
130         "     <key>DocSetPublisherName</key>\n"
131         "     <string>" << publisherName << "</string>\n"
132         "</dict>\n"
133         "</plist>\n";
134   }
135
136   // -- start Nodes.xml
137   QCString notes = Config_getString("HTML_OUTPUT") + "/Nodes.xml";
138   m_nf = new QFile(notes);
139   if (!m_nf->open(IO_WriteOnly))
140   {
141     err("Could not open file %s for writing\n",notes.data());
142     exit(1);
143   }
144   //QCString indexName=Config_getBool("GENERATE_TREEVIEW")?"main":"index";
145   QCString indexName="index";
146   m_nts.setDevice(m_nf);
147   m_nts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
148   m_nts << "<DocSetNodes version=\"1.0\">" << endl;
149   m_nts << "  <TOC>" << endl;
150   m_nts << "    <Node>" << endl;
151   m_nts << "      <Name>Root</Name>" << endl;
152   m_nts << "      <Path>" << indexName << Doxygen::htmlFileExtension << "</Path>" << endl;
153   m_nts << "      <Subnodes>" << endl;
154   m_dc = 1;
155   m_firstNode.resize(m_dc);
156   m_firstNode.at(0)=TRUE;
157
158   QCString tokens = Config_getString("HTML_OUTPUT") + "/Tokens.xml";
159   m_tf = new QFile(tokens);
160   if (!m_tf->open(IO_WriteOnly))
161   {
162     err("Could not open file %s for writing\n",tokens.data());
163     exit(1);
164   }
165   m_tts.setDevice(m_tf);
166   m_tts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
167   m_tts << "<Tokens version=\"1.0\">" << endl;
168 }
169
170 void DocSets::finalize()
171 {
172   if (!m_firstNode.at(m_dc-1))
173   {
174     m_nts << indent() << " </Node>" << endl;
175   }
176   m_dc--;
177   m_nts << "      </Subnodes>" << endl;
178   m_nts << "    </Node>" << endl;
179   m_nts << "  </TOC>" << endl;
180   m_nts << "</DocSetNodes>" << endl;
181   m_nf->close();
182   delete m_nf;
183   m_nf=0;
184
185   m_tts << "</Tokens>" << endl;
186   m_tf->close();
187   delete m_tf;
188   m_tf=0;
189 }
190
191 QCString DocSets::indent()
192 {
193   QCString result;
194   result.fill(' ',(m_dc+2)*2);
195   return result;
196 }
197
198 void DocSets::incContentsDepth()
199 {
200   //printf("DocSets::incContentsDepth() m_dc=%d\n",m_dc);
201   ++m_dc;
202   m_nts << indent() << "<Subnodes>" << endl;
203   m_firstNode.resize(m_dc);
204   if (m_dc>0)
205   {
206     m_firstNode.at(m_dc-1)=TRUE;
207   }
208 }
209
210 void DocSets::decContentsDepth()
211 {
212   if (!m_firstNode.at(m_dc-1))
213   {
214     m_nts << indent() << " </Node>" << endl;
215   }
216   m_nts << indent() << "</Subnodes>" << endl;
217   --m_dc;
218   //printf("DocSets::decContentsDepth() m_dc=%d\n",m_dc);
219 }
220
221 void DocSets::addContentsItem(bool isDir,
222                               const char *name, 
223                               const char *ref, 
224                               const char *file,
225                               const char *anchor,
226                               bool /* separateIndex */,
227                               bool /* addToNavIndex */,
228                               Definition * /*def*/)
229 {
230   (void)isDir;
231   //printf("DocSets::addContentsItem(%s) m_dc=%d\n",name,m_dc);
232   if (ref==0)
233   {
234     if (!m_firstNode.at(m_dc-1))
235     {
236       m_nts << indent() << " </Node>" << endl;
237     }
238     m_firstNode.at(m_dc-1)=FALSE;
239     m_nts << indent() << " <Node>" << endl;
240     m_nts << indent() << "  <Name>" << convertToXML(name) << "</Name>" << endl;
241     if (file && file[0]=='^') // URL marker
242     {
243       m_nts << indent() << "  <URL>" << convertToXML(&file[1]) 
244             << "</URL>" << endl;
245     }
246     else // relative file
247     {
248       m_nts << indent() << "  <Path>";
249       if (file && file[0]=='!') // user specified file
250       {
251         m_nts << convertToXML(&file[1]);
252       }
253       else if (file) // doxygen generated file
254       {
255         m_nts << file << Doxygen::htmlFileExtension;
256       }
257       m_nts << "</Path>" << endl;
258       if (file && anchor)
259       {
260         m_nts << indent() << "  <Anchor>" << anchor << "</Anchor>" << endl;
261       }
262     }
263   }
264 }
265
266 void DocSets::addIndexItem(Definition *context,MemberDef *md,const char *)
267 {
268   if (md==0 && context==0) return;
269
270   FileDef *fd      = 0;
271   ClassDef *cd     = 0;
272   NamespaceDef *nd = 0;
273
274   if (md)
275   {
276     fd = md->getFileDef();
277     cd = md->getClassDef();
278     nd = md->getNamespaceDef();
279     if (!md->isLinkable()) return; // internal symbol
280   }
281
282   QCString scope;
283   QCString type;
284   QCString decl;
285
286   // determine language
287   QCString lang;
288   SrcLangExt langExt = SrcLangExt_Cpp;
289   if (md) 
290   {
291     langExt = md->getLanguage();
292   }
293   else if (context) 
294   {
295     langExt = context->getLanguage();
296   }
297   switch (langExt)
298   {
299     case SrcLangExt_Cpp:
300     case SrcLangExt_ObjC:
301       {
302         if (md && (md->isObjCMethod() || md->isObjCProperty()))
303           lang="occ";  // Objective C/C++
304         else if (fd && fd->name().right(2).lower()==".c") 
305           lang="c";    // Plain C
306         else if (cd==0 && nd==0)
307           lang="c";    // Plain C symbol outside any class or namespace
308         else
309           lang="cpp";  // C++
310       }
311       break;
312     case SrcLangExt_IDL:     lang="idl"; break;        // IDL
313     case SrcLangExt_CSharp:  lang="csharp"; break;     // C#
314     case SrcLangExt_PHP:     lang="php"; break;        // PHP4/5
315     case SrcLangExt_D:       lang="d"; break;          // D
316     case SrcLangExt_Java:    lang="java"; break;       // Java
317     case SrcLangExt_JS:      lang="javascript"; break; // Javascript
318     case SrcLangExt_Python:  lang="python"; break;     // Python
319     case SrcLangExt_Fortran: lang="fortran"; break;    // Fortran
320     case SrcLangExt_VHDL:    lang="vhdl"; break;       // VHDL
321     case SrcLangExt_XML:     lang="xml"; break;        // DBUS XML
322     case SrcLangExt_Tcl:     lang="tcl"; break;        // Tcl
323     case SrcLangExt_Markdown:lang="markdown"; break;   // Markdown
324     case SrcLangExt_Unknown: lang="unknown"; break;    // should not happen!
325   }
326
327   if (md)
328   {
329     if (context==0)
330     {
331       if (md->getGroupDef())
332         context = md->getGroupDef();
333       else if (md->getFileDef())
334         context = md->getFileDef();
335     }
336     if (context==0) return; // should not happen
337
338     switch (md->memberType())
339     {
340       case MemberDef::Define:
341         type="macro"; break;
342       case MemberDef::Function:
343         if (cd && (cd->compoundType()==ClassDef::Interface ||
344               cd->compoundType()==ClassDef::Class))
345         {
346           if (md->isStatic())
347             type="clm";         // class member
348           else
349             type="instm";       // instance member
350         }
351         else if (cd && cd->compoundType()==ClassDef::Protocol)
352         {
353           if (md->isStatic())
354             type="intfcm";     // interface class member
355           else
356             type="intfm";      // interface member
357         }
358         else
359           type="func";
360         break;
361       case MemberDef::Variable:
362         type="data"; break;
363       case MemberDef::Typedef:
364         type="tdef"; break;
365       case MemberDef::Enumeration:
366         type="enum"; break;
367       case MemberDef::EnumValue:
368         type="econst"; break;
369         //case MemberDef::Prototype:
370         //  type="prototype"; break;
371       case MemberDef::Signal:
372         type="signal"; break;
373       case MemberDef::Slot:
374         type="slot"; break;
375       case MemberDef::Friend:
376         type="ffunc"; break;
377       case MemberDef::DCOP:
378         type="dcop"; break;
379       case MemberDef::Property:
380         if (cd && cd->compoundType()==ClassDef::Protocol) 
381           type="intfp";         // interface property
382         else 
383           type="instp";         // instance property
384         break;
385       case MemberDef::Event:
386         type="event"; break;
387     }
388     cd = md->getClassDef();
389     nd = md->getNamespaceDef();
390     if (cd) 
391     {
392       scope = cd->qualifiedName();
393     }
394     else if (nd)
395     {
396       scope = nd->name();
397     }
398     MemberDef *declMd = md->memberDeclaration();
399     if (declMd==0) declMd = md;
400     {
401       fd = md->getFileDef();
402       if (fd)
403       {
404         decl = fd->name();
405       }
406     }
407     writeToken(m_tts,md,type,lang,scope,md->anchor(),decl);
408   }
409   else if (context && context->isLinkable())
410   {
411     if (fd==0 && context->definitionType()==Definition::TypeFile)
412     {
413       fd = (FileDef*)context;
414     }
415     if (cd==0 && context->definitionType()==Definition::TypeClass)
416     {
417       cd = (ClassDef*)context;
418     }
419     if (nd==0 && context->definitionType()==Definition::TypeNamespace)
420     {
421       nd = (NamespaceDef*)context;
422     }
423     if (fd)
424     {
425       type="file";
426     }
427     else if (cd) 
428     {
429       scope = cd->qualifiedName();
430       if (cd->isTemplate())
431       {
432         type="tmplt";
433       }
434       else if (cd->compoundType()==ClassDef::Protocol) 
435       {
436         type="intf";
437         if (scope.right(2)=="-p") scope=scope.left(scope.length()-2);
438       }
439       else if (cd->compoundType()==ClassDef::Interface)
440       {
441         type="cl";
442       }
443       else if (cd->compoundType()==ClassDef::Category)
444       {
445         type="cat";
446       }
447       else 
448       {
449         type = "cl";
450       }
451       IncludeInfo *ii = cd->includeInfo();
452       if (ii)
453       {
454         decl=ii->includeName;
455         if (decl.isEmpty())
456         {
457           decl=ii->local;
458         }
459       }
460     }
461     else if (nd)
462     {
463       scope = nd->name();
464       type = "ns";
465     }
466     if (m_scopes.find(context->getOutputFileBase())==0)
467     {
468       writeToken(m_tts,context,type,lang,scope,0,decl);
469       m_scopes.append(context->getOutputFileBase(),(void*)0x8);
470     }
471   }
472 }
473
474 void DocSets::writeToken(FTextStream &t,
475                          const Definition *d,
476                          const QCString &type,
477                          const QCString &lang,
478                          const char *scope,
479                          const char *anchor,
480                          const char *decl)
481 {
482   t << "  <Token>" << endl;
483   t << "    <TokenIdentifier>" << endl;
484   QCString name = d->name();
485   if (name.right(2)=="-p")  name=name.left(name.length()-2);
486   t << "      <Name>" << convertToXML(name) << "</Name>" << endl;
487   if (!lang.isEmpty())
488   {
489     t << "      <APILanguage>" << lang << "</APILanguage>" << endl;
490   }
491   if (!type.isEmpty())
492   {
493     t << "      <Type>" << type << "</Type>" << endl;
494   }
495   if (scope)
496   {
497     t << "      <Scope>" << convertToXML(scope) << "</Scope>" << endl;
498   }
499   t << "    </TokenIdentifier>" << endl;
500   t << "    <Path>" << d->getOutputFileBase() 
501                     << Doxygen::htmlFileExtension << "</Path>" << endl;
502   if (anchor)
503   {
504     t << "    <Anchor>" << anchor << "</Anchor>" << endl;
505   }
506   QCString tooltip = d->briefDescriptionAsTooltip();
507   if (!tooltip.isEmpty())
508   {
509     t << "    <Abstract>" << convertToXML(tooltip) << "</Abstract>" << endl;
510   }
511   if (decl)
512   {
513     t << "    <DeclaredIn>" << convertToXML(decl) << "</DeclaredIn>" << endl;
514   }
515   t << "  </Token>" << endl;
516 }
517
518 void DocSets::addIndexFile(const char *name)
519 {
520   (void)name;
521 }
522