cite.cpp 9.12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/******************************************************************************
 *
 * Copyright (C) 2011 by Dimitri van Heesch
 * Based on a patch by David Munger
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation under the terms of the GNU General Public License is hereby 
 * granted. No representations are made about the suitability of this software 
 * for any purpose. It is provided "as is" without express or implied warranty.
 * See the GNU General Public License for more details.
 *
 * Documents produced by Doxygen are derivative works derived from the
 * input used in their production; they are not affected by this license.
 *
 */

#include "cite.h"
#include "portable.h"
#include "config.h"
#include "message.h"
#include "util.h"
#include "language.h"
#include "ftextstream.h"
#include <qdir.h>

//--------------------------------------------------------------------------

static const char *doxygen_bst =
29
#include "doxygen.bst.h"
30 31
;

32
static const char *bib2xhtml_pl =
33
#include "bib2xhtml.pl.h"
34 35
;

36 37 38 39
//--------------------------------------------------------------------------

const QCString CiteConsts::fileName("citelist");
const QCString CiteConsts::anchorPrefix("CITEREF_");
40 41
const QCString bibTmpFile("bibTmpFile_");
const QCString bibTmpDir("bibTmpDir/");
42 43 44 45 46 47 48 49 50 51

//--------------------------------------------------------------------------

CiteDict::CiteDict(int size) : m_entries(size, FALSE)
{ 
  m_entries.setAutoDelete(TRUE);
}

void CiteDict::writeLatexBibliography(FTextStream &t)
{
52 53 54
  if (m_entries.isEmpty())
    return;

55
  QCString style = Config_getString("LATEX_BIB_STYLE");
56 57
  if (style.isEmpty())
    style="plain";
58 59 60 61 62
  QCString unit;
  if (Config_getBool("COMPACT_LATEX"))
    unit = "section";
  else
    unit = "chapter";
63 64
  t << "% Bibliography\n"
       "\\newpage\n"
albert-github's avatar
albert-github committed
65 66 67 68 69 70 71 72
       "\\phantomsection\n";
  bool pdfHyperlinks = Config_getBool("PDF_HYPERLINKS");
  if (!pdfHyperlinks)
  {
    t << "\\clearemptydoublepage\n";
    t << "\\addcontentsline{toc}{" << unit << "}{" << theTranslator->trCiteReferences() << "}\n";
  }
  t << "\\bibliographystyle{" << style << "}\n"
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
       "\\bibliography{";
  QStrList &citeDataList = Config_getList("CITE_BIB_FILES");
  QCString latexOutputDir = Config_getString("LATEX_OUTPUT")+"/";
  int i = 0;
  const char *bibdata = citeDataList.first();
  while (bibdata)
  {
    QCString bibFile = bibdata;
    // Note: file can now have multiple dots
    if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib";
    QFileInfo fi(bibFile);
    if (fi.exists())
    {
      if (!bibFile.isEmpty())
      {
        if (i) t << ",";
        i++;
        t << bibTmpFile << QString().setNum(i);
      }
    }
    bibdata = citeDataList.next();
  }
albert-github's avatar
albert-github committed
95 96 97 98 99 100
  t << "}\n";
  if (pdfHyperlinks)
  {
    t << "\\addcontentsline{toc}{" << unit << "}{" << theTranslator->trCiteReferences() << "}\n";
  }
  t << "\n";
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
}

void CiteDict::insert(const char *label)
{
  m_entries.insert(label,new CiteInfo(label));
}

CiteInfo *CiteDict::find(const char *label) const
{
  return label ? m_entries.find(label) : 0;
}

void CiteDict::clear()
{
  m_entries.clear();
}

118
bool CiteDict::isEmpty() const
119
{
120 121
  QStrList &citeBibFiles = Config_getList("CITE_BIB_FILES");
  return (citeBibFiles.count()==0 || m_entries.isEmpty());
122 123
}

124
void CiteDict::generatePage() const
125
{
126
  //printf("** CiteDict::generatePage() count=%d\n",m_ordering.count());
127

128 129
  // do not generate an empty citations page
  if (isEmpty()) return; // nothing to cite
130

131 132 133 134 135 136
  // 1. generate file with markers and citations to OUTPUT_DIRECTORY
  QFile f;
  QCString outputDir = Config_getString("OUTPUT_DIRECTORY");
  QCString citeListFile = outputDir+"/citelist.doc";
  f.setName(citeListFile);
  if (!f.open(IO_WriteOnly)) 
137
  {
138
    err("could not open file %s for writing\n",citeListFile.data());
139
  }
140 141 142 143 144 145
  FTextStream t(&f);
  t << "<!-- BEGIN CITATIONS -->" << endl;
  t << "<!--" << endl;
  QDictIterator<CiteInfo> it(m_entries);
  CiteInfo *ci;
  for (it.toFirst();(ci=it.current());++it)
146
  {
147
    t << "\\citation{" << ci->label << "}" << endl;
148
  }
149 150 151 152 153 154 155 156 157 158 159
  t << "-->" << endl;
  t << "<!-- END CITATIONS -->" << endl;
  t << "<!-- BEGIN BIBLIOGRAPHY -->" << endl;
  t << "<!-- END BIBLIOGRAPHY -->" << endl;
  f.close();

  // 2. generate bib2xhtml
  QCString bib2xhtmlFile = outputDir+"/bib2xhtml.pl";
  f.setName(bib2xhtmlFile);
  QCString bib2xhtml = bib2xhtml_pl;
  if (!f.open(IO_WriteOnly)) 
160
  {
161
    err("could not open file %s for writing\n",bib2xhtmlFile.data());
162
  }
163 164
  f.writeBlock(bib2xhtml, bib2xhtml.length());
  f.close();
165

166 167 168 169 170 171
  // 3. generate doxygen.bst
  QCString doxygenBstFile = outputDir+"/doxygen.bst";
  QCString bstData = doxygen_bst;
  f.setName(doxygenBstFile);
  if (!f.open(IO_WriteOnly)) 
  {
172
    err("could not open file %s for writing\n",doxygenBstFile.data());
173 174 175
  }
  f.writeBlock(bstData, bstData.length());
  f.close();
176

177
  // 4. for all formats we just copy the bib files to as special output directory
178
  //    so bibtex can find them without path (bibtex doesn't support paths or
179 180 181 182 183
  //    filenames with spaces!)
  //    Strictly not required when only latex is generated
  QStrList &citeDataList = Config_getList("CITE_BIB_FILES");
  QCString bibOutputDir = outputDir+"/"+bibTmpDir;
  QCString bibOutputFiles = "";
184
  QDir thisDir;
185 186 187 188
  thisDir.mkdir(bibOutputDir);
  const char *bibdata = citeDataList.first();
  int i = 0;
  while (bibdata)
189
  {
190 191 192 193
    QCString bibFile = bibdata;
    if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib";
    QFileInfo fi(bibFile);
    if (fi.exists())
194
    {
195
      if (!bibFile.isEmpty())
196
      {
197 198 199
        ++i;
        copyFile(bibFile,bibOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
        bibOutputFiles = bibOutputFiles + " " + bibTmpDir + bibTmpFile + QCString().setNum(i) + ".bib";
200 201
      }
    }
202 203 204 205 206
    else if (!fi.exists())
    {
      err("bib file %s not found!\n",bibFile.data());
    }
    bibdata = citeDataList.next();
207 208
  }

209
  QString oldDir = QDir::currentDirPath();
210 211 212
  QDir::setCurrent(outputDir);

  // 5. run bib2xhtml perl script on the generated file which will insert the
213
  //    bibliography in citelist.doc
214
  portable_system("perl","\""+bib2xhtmlFile+"\" "+bibOutputFiles+" \""+
215
                         citeListFile+"\"");
216

217 218 219
  QDir::setCurrent(oldDir);

  // 6. read back the file
220 221
  f.setName(citeListFile);
  if (!f.open(IO_ReadOnly)) 
222
  {
223
    err("could not open file %s for reading\n",citeListFile.data());
224
  }
225 226 227 228 229 230
  bool insideBib=FALSE;
  
  QCString doc;
  QFileInfo fi(citeListFile);
  QCString input(fi.size()+1);
  f.readBlock(input.data(),fi.size());
231
  f.close();
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
  input.at(fi.size())='\0';
  int p=0,s;
  //printf("input=[%s]\n",input.data());
  while ((s=input.find('\n',p))!=-1)
  {
    QCString line = input.mid(p,s-p);
    //printf("p=%d s=%d line=[%s]\n",p,s,line.data());
    p=s+1;

    if      (line.find("<!-- BEGIN BIBLIOGRAPHY")!=-1) insideBib=TRUE;
    else if (line.find("<!-- END BIBLIOGRAPH")!=-1)    insideBib=FALSE;
    else if (insideBib) doc+=line+"\n";
    int i;
    // determine text to use at the location of the @cite command
    if (insideBib && (i=line.find("<a name=\"CITEREF_"))!=-1)
    {
      int j=line.find("\">[");
      int k=line.find("]</a>");
      if (j!=-1 && k!=-1)
      {
        QCString label = line.mid(i+17,j-i-17);
        QCString number = line.mid(j+2,k-j-1);
        CiteInfo *ci = m_entries.find(label);
        //printf("label='%s' number='%s' => %p\n",label.data(),number.data(),ci);
        if (ci)
        {
          ci->text = number;
        }
      }
    }
  }
  //printf("doc=[%s]\n",doc.data());

265
  // 7. add it as a page
266 267
  addRelatedPage(CiteConsts::fileName,
       theTranslator->trCiteReferences(),doc,0,CiteConsts::fileName,1,0,0,0);
268

269
  // 8. for latex we just copy the bib files to the output and let 
270
  //    latex do this work.
271 272 273 274 275
  if (Config_getBool("GENERATE_LATEX"))
  {
    // copy bib files to the latex output dir
    QStrList &citeDataList = Config_getList("CITE_BIB_FILES");
    QCString latexOutputDir = Config_getString("LATEX_OUTPUT")+"/";
276
    int i = 0;
277 278 279 280
    const char *bibdata = citeDataList.first();
    while (bibdata)
    {
      QCString bibFile = bibdata;
281
      // Note: file can now have multiple dots
282
      if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib";
283 284 285 286 287
      QFileInfo fi(bibFile);
      if (fi.exists())
      {
        if (!bibFile.isEmpty())
        {
288 289 290 291
          // bug_700510, multile times the same name were overwriting; creating new names
          // also for names with spaces
          ++i;
          copyFile(bibFile,latexOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
292 293 294
        }
      }
      else
295
      {
296
        err("bib file %s not found!\n",bibFile.data());
297 298 299 300 301
      }
      bibdata = citeDataList.next();
    }
  }

302
  // 9. Remove temporary files
303 304 305
  thisDir.remove(citeListFile);
  thisDir.remove(doxygenBstFile);
  thisDir.remove(bib2xhtmlFile);
306 307 308 309 310
  bibdata = citeDataList.first();
  // we might try to remove too many files as empty files didn't get a coresponding new file
  // but the remove function does not emit an error for it and we don't catch the error return
  // so no problem.
  for (unsigned int j = 1; j <= citeDataList.count(); j++)
311
  {
312
    thisDir.remove(bibOutputDir + bibTmpFile + QCString().setNum(j) + ".bib");
313
  }
314
  thisDir.rmdir(bibOutputDir);
315 316
}