f86350bc96c180ebbb80335cc63f86d7826f743b
[platform/upstream/doxygen.git] / src / cite.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 2011 by Dimitri van Heesch
4  * Based on a patch by David Munger
5  *
6  * Permission to use, copy, modify, and distribute this software and its
7  * documentation under the terms of the GNU General Public License is hereby 
8  * granted. No representations are made about the suitability of this software 
9  * for any purpose. It is provided "as is" without express or implied warranty.
10  * See the GNU General Public License for more details.
11  *
12  * Documents produced by Doxygen are derivative works derived from the
13  * input used in their production; they are not affected by this license.
14  *
15  */
16
17 #include "cite.h"
18 #include "portable.h"
19 #include "config.h"
20 #include "message.h"
21 #include "util.h"
22 #include "language.h"
23 #include "ftextstream.h"
24 #include "resourcemgr.h"
25 #include <qdir.h>
26
27 //--------------------------------------------------------------------------
28
29 const QCString CiteConsts::fileName("citelist");
30 const QCString CiteConsts::anchorPrefix("CITEREF_");
31 const QCString bibTmpFile("bibTmpFile_");
32 const QCString bibTmpDir("bibTmpDir/");
33
34 //--------------------------------------------------------------------------
35
36 CiteDict::CiteDict(int size) : m_entries(size, FALSE)
37
38   m_entries.setAutoDelete(TRUE);
39 }
40
41 void CiteDict::writeLatexBibliography(FTextStream &t)
42 {
43   if (m_entries.isEmpty())
44     return;
45
46   QCString style = Config_getString(LATEX_BIB_STYLE);
47   if (style.isEmpty())
48     style="plain";
49   QCString unit;
50   if (Config_getBool(COMPACT_LATEX))
51     unit = "section";
52   else
53     unit = "chapter";
54   t << "% Bibliography\n"
55        "\\newpage\n"
56        "\\phantomsection\n";
57   bool pdfHyperlinks = Config_getBool(PDF_HYPERLINKS);
58   if (!pdfHyperlinks)
59   {
60     t << "\\clearemptydoublepage\n";
61     t << "\\addcontentsline{toc}{" << unit << "}{" << theTranslator->trCiteReferences() << "}\n";
62   }
63   t << "\\bibliographystyle{" << style << "}\n"
64        "\\bibliography{";
65   QStrList &citeDataList = Config_getList(CITE_BIB_FILES);
66   int i = 0;
67   const char *bibdata = citeDataList.first();
68   while (bibdata)
69   {
70     QCString bibFile = bibdata;
71     // Note: file can now have multiple dots
72     if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib";
73     QFileInfo fi(bibFile);
74     if (fi.exists())
75     {
76       if (!bibFile.isEmpty())
77       {
78         if (i) t << ",";
79         i++;
80         t << bibTmpFile << QString().setNum(i);
81       }
82     }
83     bibdata = citeDataList.next();
84   }
85   t << "}\n";
86   if (pdfHyperlinks)
87   {
88     t << "\\addcontentsline{toc}{" << unit << "}{" << theTranslator->trCiteReferences() << "}\n";
89   }
90   t << "\n";
91 }
92
93 void CiteDict::insert(const char *label)
94 {
95   m_entries.insert(label,new CiteInfo(label));
96 }
97
98 CiteInfo *CiteDict::find(const char *label) const
99 {
100   return label ? m_entries.find(label) : 0;
101 }
102
103 void CiteDict::clear()
104 {
105   m_entries.clear();
106 }
107
108 bool CiteDict::isEmpty() const
109 {
110   QStrList &citeBibFiles = Config_getList(CITE_BIB_FILES);
111   return (citeBibFiles.count()==0 || m_entries.isEmpty());
112 }
113
114 void CiteDict::generatePage() const
115 {
116   //printf("** CiteDict::generatePage() count=%d\n",m_ordering.count());
117
118   // do not generate an empty citations page
119   if (isEmpty()) return; // nothing to cite
120
121   // 1. generate file with markers and citations to OUTPUT_DIRECTORY
122   QFile f;
123   QCString outputDir = Config_getString(OUTPUT_DIRECTORY);
124   QCString citeListFile = outputDir+"/citelist.doc";
125   f.setName(citeListFile);
126   if (!f.open(IO_WriteOnly)) 
127   {
128     err("could not open file %s for writing\n",citeListFile.data());
129   }
130   FTextStream t(&f);
131   t << "<!-- BEGIN CITATIONS -->" << endl;
132   t << "<!--" << endl;
133   QDictIterator<CiteInfo> it(m_entries);
134   CiteInfo *ci;
135   for (it.toFirst();(ci=it.current());++it)
136   {
137     t << "\\citation{" << ci->label << "}" << endl;
138   }
139   t << "-->" << endl;
140   t << "<!-- END CITATIONS -->" << endl;
141   t << "<!-- BEGIN BIBLIOGRAPHY -->" << endl;
142   t << "<!-- END BIBLIOGRAPHY -->" << endl;
143   f.close();
144
145   // 2. generate bib2xhtml
146   QCString bib2xhtmlFile  = outputDir+"/bib2xhtml.pl";
147   ResourceMgr::instance().copyResource("bib2xhtml.pl",outputDir);
148
149   // 3. generate doxygen.bst
150   QCString doxygenBstFile = outputDir+"/doxygen.bst";
151   ResourceMgr::instance().copyResource("doxygen.bst",outputDir);
152
153   // 4. for all formats we just copy the bib files to as special output directory
154   //    so bibtex can find them without path (bibtex doesn't support paths or
155   //    filenames with spaces!)
156   //    Strictly not required when only latex is generated
157   QStrList &citeDataList = Config_getList(CITE_BIB_FILES);
158   QCString bibOutputDir = outputDir+"/"+bibTmpDir;
159   QCString bibOutputFiles = "";
160   QDir thisDir;
161   thisDir.mkdir(bibOutputDir);
162   const char *bibdata = citeDataList.first();
163   int i = 0;
164   while (bibdata)
165   {
166     QCString bibFile = bibdata;
167     if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib";
168     QFileInfo fi(bibFile);
169     if (fi.exists())
170     {
171       if (!bibFile.isEmpty())
172       {
173         ++i;
174         copyFile(bibFile,bibOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
175         bibOutputFiles = bibOutputFiles + " " + bibTmpDir + bibTmpFile + QCString().setNum(i) + ".bib";
176       }
177     }
178     else if (!fi.exists())
179     {
180       err("bib file %s not found!\n",bibFile.data());
181     }
182     bibdata = citeDataList.next();
183   }
184
185   QString oldDir = QDir::currentDirPath();
186   QDir::setCurrent(outputDir);
187
188   // 5. run bib2xhtml perl script on the generated file which will insert the
189   //    bibliography in citelist.doc
190   int exitCode;
191   portable_sysTimerStop();
192   if ((exitCode=portable_system("perl","\""+bib2xhtmlFile+"\" "+bibOutputFiles+" \""+
193                          citeListFile+"\"")) != 0)
194   {
195     err("Problems running bibtex. Verify that the command 'perl --version' works from the command line. Exit code: %d\n",
196         exitCode);
197   }
198   portable_sysTimerStop();
199
200   QDir::setCurrent(oldDir);
201
202   // 6. read back the file
203   f.setName(citeListFile);
204   if (!f.open(IO_ReadOnly)) 
205   {
206     err("could not open file %s for reading\n",citeListFile.data());
207   }
208   bool insideBib=FALSE;
209   
210   QCString doc;
211   QFileInfo fi(citeListFile);
212   QCString input(fi.size()+1);
213   f.readBlock(input.rawData(),fi.size());
214   f.close();
215   input.at(fi.size())='\0';
216   int p=0,s;
217   //printf("input=[%s]\n",input.data());
218   while ((s=input.find('\n',p))!=-1)
219   {
220     QCString line = input.mid(p,s-p);
221     //printf("p=%d s=%d line=[%s]\n",p,s,line.data());
222     p=s+1;
223
224     if      (line.find("<!-- BEGIN BIBLIOGRAPHY")!=-1) insideBib=TRUE;
225     else if (line.find("<!-- END BIBLIOGRAPH")!=-1)    insideBib=FALSE;
226     else if (insideBib) doc+=line+"\n";
227     int i;
228     // determine text to use at the location of the @cite command
229     if (insideBib && (i=line.find("name=\"CITEREF_"))!=-1)
230     {
231       int j=line.find("\">[");
232       int k=line.find("]</a>");
233       if (j!=-1 && k!=-1)
234       {
235         QCString label = line.mid(i+14,j-i-14);
236         QCString number = line.mid(j+2,k-j-1);
237         CiteInfo *ci = m_entries.find(label);
238         //printf("label='%s' number='%s' => %p\n",label.data(),number.data(),ci);
239         if (ci)
240         {
241           ci->text = number;
242         }
243       }
244     }
245   }
246   //printf("doc=[%s]\n",doc.data());
247
248   // 7. add it as a page
249   addRelatedPage(CiteConsts::fileName,
250        theTranslator->trCiteReferences(),doc,0,CiteConsts::fileName,1,0,0,0);
251
252   // 8. for latex we just copy the bib files to the output and let 
253   //    latex do this work.
254   if (Config_getBool(GENERATE_LATEX))
255   {
256     // copy bib files to the latex output dir
257     QStrList &citeDataList = Config_getList(CITE_BIB_FILES);
258     QCString latexOutputDir = Config_getString(LATEX_OUTPUT)+"/";
259     int i = 0;
260     const char *bibdata = citeDataList.first();
261     while (bibdata)
262     {
263       QCString bibFile = bibdata;
264       // Note: file can now have multiple dots
265       if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib";
266       QFileInfo fi(bibFile);
267       if (fi.exists())
268       {
269         if (!bibFile.isEmpty())
270         {
271           // bug_700510, multile times the same name were overwriting; creating new names
272           // also for names with spaces
273           ++i;
274           copyFile(bibFile,latexOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
275         }
276       }
277       else
278       {
279         err("bib file %s not found!\n",bibFile.data());
280       }
281       bibdata = citeDataList.next();
282     }
283   }
284
285   // 9. Remove temporary files
286   thisDir.remove(citeListFile);
287   thisDir.remove(doxygenBstFile);
288   thisDir.remove(bib2xhtmlFile);
289   // we might try to remove too many files as empty files didn't get a corresponding new file
290   // but the remove function does not emit an error for it and we don't catch the error return
291   // so no problem.
292   for (unsigned int j = 1; j <= citeDataList.count(); j++)
293   {
294     thisDir.remove(bibOutputDir + bibTmpFile + QCString().setNum(j) + ".bib");
295   }
296   thisDir.rmdir(bibOutputDir);
297 }
298