Doxygen
Loading...
Searching...
No Matches
cite.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 2020 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 "config.h"
19#include "language.h"
20#include "message.h"
21#include "portable.h"
22#include "resourcemgr.h"
23#include "util.h"
24#include "debug.h"
25#include "fileinfo.h"
26#include "dir.h"
27#include "growbuf.h"
28#include "entry.h"
29#include "commentscan.h"
30#include "linkedmap.h"
31
32#include <map>
33#include <unordered_map>
34#include <string>
35#include <fstream>
36
37const char *bibTmpFile = "bibTmpFile_";
38const char *bibTmpDir = "bibTmpDir/";
39
40//! class that provide information about the p[osition of a citation name
42{
43 public:
44 CitePosition(const QCString &fn, int l) : fileName(fn), lineNr(l) {}
45
46 QCString fileName;
47 int lineNr;
48};
49
50static QCString getBibFile(const QCString &inFile)
51{
52 QCString name = inFile;
53 if (!name.isEmpty() && !name.endsWith(".bib")) name+=".bib";
54 return name;
55}
56
57class CiteInfoImpl : public CiteInfo
58{
59 public:
60 CiteInfoImpl(const QCString &label, const QCString &text=QCString())
61 : m_label(label), m_text(text) { }
62
63 QCString label() const override { return m_label; }
64 QCString text() const override { return m_text; }
65
66 void setText(const QCString &s) { m_text = s; }
67
68 private:
69 QCString m_label;
70 QCString m_text;
71};
72
74{
75 std::map< std::string,std::unique_ptr<CiteInfoImpl> > entries;
76 std::unordered_map< int,std::string > formulaCite;
77 std::unordered_map< std::string, CitePosition > citePosition;
78};
79
81{
82 static CitationManager ct;
83 return ct;
84}
85
89
91{
92 QCString lowerCaseLabel = label.lower();
93 p->entries.emplace(lowerCaseLabel.str(),std::make_unique<CiteInfoImpl>(lowerCaseLabel));
94}
95
96const CiteInfo *CitationManager::find(const QCString &label) const
97{
98 auto it = p->entries.find(label.lower().str());
99 if (it!=p->entries.end())
100 {
101 return it->second.get();
102 }
103 return nullptr;
104}
105
107{
108 p->entries.clear();
109}
110
112{
113 size_t numFiles = Config_getList(CITE_BIB_FILES).size();
114 return (numFiles==0 || p->entries.empty());
115}
116
118{
119 return "citelist";
120}
121
123{
124 return "CITEREF_";
125}
126
128{
129 // sanity checks
130 if (bibFile.isEmpty())
131 {
132 return;
133 }
134 FileInfo fi(bibFile.str());
135 if (!fi.exists())
136 {
137 err("bib file %s not found!\n",qPrint(bibFile));
138 return;
139 }
140 std::ifstream f = Portable::openInputStream(bibFile);
141 if (!f.is_open())
142 {
143 err("could not open file %s for reading\n",qPrint(bibFile));
144 return;
145 }
146
147 // search for citation cross references
148 QCString citeName;
149
150 std::string lineStr;
151 int lineCount = 0;
152 while (getline(f,lineStr))
153 {
154 int i = -1;
155 QCString line(lineStr);
156 lineCount++;
157 if (line.stripWhiteSpace().startsWith("@"))
158 {
159 // assumption entry like: "@book { name," or "@book { name" (spaces optional)
160 int j = line.find('{');
161 // when no {, go hunting for it
162 while (j==-1 && getline(f,lineStr))
163 {
164 line = lineStr;
165 lineCount++;
166 j = line.find('{');
167 }
168 // search for the name
169 citeName = "";
170 if (!f.eof() && j!=-1) // to prevent something like "@manual ," and no { found
171 {
172 int k = line.find(',',j);
173 j++;
174 // found a line "@....{.....,...." or "@.....{....."
175 // ^=j ^=k ^=j k=-1
176 while (!f.eof() && citeName.isEmpty())
177 {
178 if (k!=-1)
179 {
180 citeName = line.mid(static_cast<size_t>(j),static_cast<size_t>(k-j));
181 }
182 else
183 {
184 citeName = line.mid(static_cast<size_t>(j));
185 }
186 citeName = citeName.stripWhiteSpace();
187 j = 0;
188 if (citeName.isEmpty() && getline(f,lineStr))
189 {
190 line = lineStr;
191 lineCount++;
192 k = line.find(',');
193 }
194 }
195 }
196 //printf("citeName = #%s#\n",qPrint(citeName));
197 if (!citeName.isEmpty())
198 {
199 std::string lCiteName = citeName.lower().str();
200 auto it = p->citePosition.find(lCiteName);
201 if (it != p->citePosition.end())
202 {
203 warn(bibFile,lineCount,"multiple use of citation name '%s', (first occurrence: %s, line %d)",
204 qPrint(lCiteName),qPrint(it->second.fileName),it->second.lineNr);
205 }
206 else
207 {
208 p->citePosition.emplace(lCiteName,CitePosition(bibFile,lineCount));
209 }
210 }
211 }
212 else if ((i=line.find("crossref"))!=-1 && !citeName.isEmpty()) /* assumption cross reference is on one line and the only item */
213 {
214 int j = line.find('{',i);
215 int k = line.find('}',i);
216 if (j>i && k>j)
217 {
218 QCString crossrefName = line.mid(static_cast<size_t>(j+1),static_cast<uint32_t>(k-j-1));
219 // check if the reference with the cross reference is used
220 // insert cross reference when cross reference has not yet been added.
221 if (find(citeName) && !find(crossrefName)) // not found yet
222 {
223 insert(crossrefName);
224 }
225 }
226 }
227 }
228}
229
230const std::string g_formulaMarker = "CITE_FORMULA_";
231
233{
234 if (s.isEmpty()) return s;
235 GrowBuf growBuf;
236 GrowBuf formulaBuf;
237 bool insideFormula = false;
238 int citeFormulaCnt = 1;
239 const size_t tmpLen = 30;
240 char tmp[tmpLen];
241 const char *ps=s.data();
242 char c = 0;
243 while ((c=*ps++))
244 {
245 if (insideFormula)
246 {
247 switch (c)
248 {
249 case '\\':
250 formulaBuf.addChar(c);
251 c = *ps++;
252 formulaBuf.addChar(c);
253 break;
254 case '\n':
255 formulaBuf.addChar(c);
256 formulaBuf.addChar(0);
257 growBuf.addChar('$');
258 growBuf.addStr(formulaBuf.get());
259 insideFormula = false;
260 formulaBuf.clear();
261 break;
262 case '$':
263 qsnprintf(tmp,tmpLen,"%s%06d",g_formulaMarker.c_str(),citeFormulaCnt);
264 formulaBuf.addChar(0);
265 p->formulaCite.emplace(citeFormulaCnt,std::string("\\f$") + formulaBuf.get() + "\\f$");
266 citeFormulaCnt++;
267 // need { and } due to the capitalization rules of bibtex.
268 growBuf.addChar('{');
269 growBuf.addStr(tmp);
270 growBuf.addChar('}');
271 insideFormula = false;
272 formulaBuf.clear();
273 break;
274 default:
275 formulaBuf.addChar(c);
276 break;
277 }
278 }
279 else
280 {
281 switch (c)
282 {
283 case '\\':
284 growBuf.addChar(c);
285 c = *ps++;
286 growBuf.addChar(c);
287 break;
288 case '$':
289 insideFormula = true;
290 break;
291 default:
292 growBuf.addChar(c);
293 break;
294 }
295 }
296 }
297 if (insideFormula)
298 {
299 formulaBuf.addChar(0);
300 growBuf.addStr(formulaBuf.get());
301 formulaBuf.clear();
302 }
303 growBuf.addChar(0);
304 return growBuf.get();
305}
306
308{
309 if (s.isEmpty()) return s;
310 QCString t;
311 int pos=0;
312 int i = -1;
313 while ((i=s.find(g_formulaMarker.c_str(),pos))!=-1)
314 {
315 t += s.mid(pos,i-pos);
316 int markerSize = static_cast<int>( g_formulaMarker.length());
317 int markerId = atoi(s.mid(i+markerSize,6).data());
318 auto it = p->formulaCite.find(markerId);
319 if (it != p->formulaCite.end()) t += it->second;
320 pos = i + markerSize+6;
321 }
322 t += s.mid(pos);
323 //printf("replaceFormulas(%s)=%s\n",qPrint(s),qPrint(t));
324 return t;
325}
326
328{
329 //printf("** CitationManager::generatePage() count=%d\n",m_ordering.count());
330
331 // do not generate an empty citations page
332 if (isEmpty()) return; // nothing to cite
333
334 bool citeDebug = Debug::isFlagSet(Debug::Cite);
335
336 // 0. add cross references from the bib files to the cite dictionary
337 const StringVector &citeDataList = Config_getList(CITE_BIB_FILES);
338 for (const auto &bibdata : citeDataList)
339 {
340 QCString bibFile = getBibFile(QCString(bibdata));
342 }
343
344 // 1. generate file with markers and citations to OUTPUT_DIRECTORY
345 QCString outputDir = Config_getString(OUTPUT_DIRECTORY);
346 QCString citeListFile = outputDir+"/citelist.doc";
347 {
348 std::ofstream t = Portable::openOutputStream(citeListFile);
349 if (!t.is_open())
350 {
351 err("could not open file %s for writing\n",qPrint(citeListFile));
352 }
353 t << "<!-- BEGIN CITATIONS -->\n";
354 t << "<!--\n";
355 for (const auto &it : p->entries)
356 {
357 t << "\\citation{" << it.second->label() << "}\n";
358 }
359 t << "-->\n";
360 t << "<!-- END CITATIONS -->\n";
361 t << "<!-- BEGIN BIBLIOGRAPHY -->\n";
362 t << "<!-- END BIBLIOGRAPHY -->\n";
363 t.close();
364 }
365
366 // 2. generate bib2xhtml
367 QCString bib2xhtmlFile = outputDir+"/bib2xhtml.pl";
368 ResourceMgr::instance().copyResource("bib2xhtml.pl",outputDir);
369
370 // 3. generate doxygen.bst
371 QCString doxygenBstFile = outputDir+"/doxygen.bst";
372 ResourceMgr::instance().copyResource("doxygen.bst",outputDir);
373
374 // 4. for all formats we just copy the bib files to as special output directory
375 // so bibtex can find them without path (bibtex doesn't support paths or
376 // filenames with spaces!)
377 // Strictly not required when only latex is generated
378 QCString bibOutputDir = outputDir+"/"+bibTmpDir;
379 QCString bibOutputFiles = "";
380 Dir thisDir;
381 if (!thisDir.exists(bibOutputDir.str()) && !thisDir.mkdir(bibOutputDir.str()))
382 {
383 err("Failed to create temporary output directory '%s', skipping citations\n",qPrint(bibOutputDir));
384 return;
385 }
386 int i = 0;
387 for (const auto &bibdata : citeDataList)
388 {
389 QCString bibFile = getBibFile(QCString(bibdata));
390 FileInfo fi(bibFile.str());
391 if (fi.exists())
392 {
393 if (!bibFile.isEmpty())
394 {
395 ++i;
396 std::ifstream f_org = Portable::openInputStream(bibFile);
397 if (!f_org.is_open())
398 {
399 err("could not open file %s for reading\n",qPrint(bibFile));
400 }
401 std::ofstream f_out = Portable::openOutputStream(bibOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
402 if (!f_out.is_open())
403 {
404 err("could not open file %s for reading\n",qPrint(bibOutputDir + bibTmpFile + QCString().setNum(i) + ".bib"));
405 }
406 QCString docs;
407 std::string lineStr;
408 while (getline(f_org,lineStr))
409 {
410 docs += lineStr + "\n";
411 }
412 docs = getFormulas(docs);
413 f_out << docs;
414 if (f_org.is_open()) f_org.close();
415 if (f_out.is_open()) f_out.close();
416 bibOutputFiles = bibOutputFiles + " " + bibTmpDir + bibTmpFile + QCString().setNum(i) + ".bib";
417 }
418 }
419 }
420
421 std::string oldDir = Dir::currentDirPath();
422 Dir::setCurrent(outputDir.str());
423
424 // 5. run bib2xhtml perl script on the generated file which will insert the
425 // bibliography in citelist.doc
426 QCString perlArgs = "\""+bib2xhtmlFile+"\" "+bibOutputFiles+" \""+ citeListFile+"\"";
427 if (citeDebug) perlArgs+=" -d";
428 int exitCode = Portable::system("perl",perlArgs);
429 if (exitCode!=0)
430 {
431 err("Problems running bibtex. Verify that the command 'perl --version' works from the command line. Exit code: %d\n",
432 exitCode);
433 }
434
435 Dir::setCurrent(oldDir);
436
437 // 6. read back the file
438 QCString doc;
439 {
440 std::ifstream f = Portable::openInputStream(citeListFile);
441 if (!f.is_open())
442 {
443 err("could not open file %s for reading\n",qPrint(citeListFile));
444 }
445
446 bool insideBib=FALSE;
447 //printf("input=[%s]\n",qPrint(input));
448 std::string lineStr;
449 while (getline(f,lineStr))
450 {
451 QCString line(lineStr);
452 //printf("pos=%d s=%d line=[%s]\n",pos,s,qPrint(line));
453
454 if (line.find("<!-- BEGIN BIBLIOGRAPHY")!=-1) insideBib=TRUE;
455 else if (line.find("<!-- END BIBLIOGRAPH")!=-1) insideBib=FALSE;
456 // determine text to use at the location of the @cite command
457 if (insideBib && ((i=line.find("name=\"CITEREF_"))!=-1 || (i=line.find("name=\"#CITEREF_"))!=-1))
458 {
459 int j=line.find("\">[");
460 int k=line.find("]</a>");
461 if (j!=-1 && k!=-1)
462 {
463 size_t ui=static_cast<size_t>(i);
464 size_t uj=static_cast<size_t>(j);
465 size_t uk=static_cast<size_t>(k);
466 QCString label = line.mid(ui+14,uj-ui-14);
467 QCString number = line.mid(uj+2,uk-uj-1);
468 line = line.left(ui+14) + label + line.right(line.length()-uj);
469 auto it = p->entries.find(label.lower().str());
470 //printf("label='%s' number='%s' => %p\n",qPrint(label),qPrint(number),it->second.get());
471 if (it!=p->entries.end())
472 {
473 it->second->setText(number);
474 }
475 }
476 }
477 if (insideBib) doc+=line+"\n";
478 }
479 //printf("doc=[%s]\n",qPrint(doc));
480 }
481
482 // 7. place formulas back and run the conversion of \f$ ... \f$ to the internal required format
483 {
484 doc = replaceFormulas(doc);
485 Entry current;
486 bool needsEntry = false;
487 CommentScanner commentScanner;
488 int lineNr = 0;
489 int pos = 0;
490 GuardedSectionStack guards;
492 commentScanner.parseCommentBlock(
493 nullptr,
494 &current,
495 doc, // text
496 fileName(), // file
497 lineNr, // line of block start
498 false, // isBrief
499 false, // isJavaDocStyle
500 false, // isInBody
501 prot, // protection
502 pos, // position,
503 needsEntry,
504 false,
505 &guards
506 );
507 doc = current.doc;
508 }
509
510 // 8. add it as a page
512
513 // 9. for latex we just copy the bib files to the output and let
514 // latex do this work.
515 if (Config_getBool(GENERATE_LATEX))
516 {
517 // copy bib files to the latex output dir
518 QCString latexOutputDir = Config_getString(LATEX_OUTPUT)+"/";
519 i = 0;
520 for (const auto &bibdata : citeDataList)
521 {
522 QCString bibFile = getBibFile(QCString(bibdata));
523 FileInfo fi(bibFile.str());
524 if (fi.exists())
525 {
526 if (!bibFile.isEmpty())
527 {
528 // bug_700510, multiple times the same name were overwriting; creating new names
529 // also for names with spaces
530 ++i;
531 copyFile(bibFile,latexOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
532 }
533 }
534 else
535 {
536 err("bib file %s not found!\n",qPrint(bibFile));
537 }
538 }
539 }
540
541 // 10. Remove temporary files
542 if (!citeDebug)
543 {
544 thisDir.remove(citeListFile.str());
545 thisDir.remove(doxygenBstFile.str());
546 thisDir.remove(bib2xhtmlFile.str());
547 // we might try to remove too many files as empty files didn't get a corresponding new file
548 // but the remove function does not emit an error for it and we don't catch the error return
549 // so no problem.
550 for (size_t j = 1; j <= citeDataList.size(); j++)
551 {
552 QCString bibFile = bibOutputDir + bibTmpFile + QCString().setNum(static_cast<int>(j)) + ".bib";
553 thisDir.remove(bibFile.str());
554 }
555 thisDir.rmdir(bibOutputDir.str());
556 }
557}
558
560{
561 QCString result;
562 const StringVector &citeDataList = Config_getList(CITE_BIB_FILES);
563 int i = 0;
564 for (const auto &bibdata : citeDataList)
565 {
566 QCString bibFile = getBibFile(QCString(bibdata));
567 FileInfo fi(bibFile.str());
568 if (fi.exists())
569 {
570 if (!bibFile.isEmpty())
571 {
572 if (i) result += ",";
573 i++;
574 result += bibTmpFile;
575 result += QCString().setNum(i);
576 }
577 }
578 }
579 return result;
580}
static QCString getBibFile(const QCString &inFile)
Definition cite.cpp:50
const std::string g_formulaMarker
Definition cite.cpp:230
const char * bibTmpFile
Definition cite.cpp:37
const char * bibTmpDir
Definition cite.cpp:38
void insertCrossReferencesForBibFile(const QCString &bibFile)
Definition cite.cpp:127
QCString anchorPrefix() const
Definition cite.cpp:122
std::unique_ptr< Private > p
Definition cite.h:78
const CiteInfo * find(const QCString &label) const
Return the citation info for a given label.
Definition cite.cpp:96
QCString latexBibFiles()
lists the bibtex cite files in a comma separated list
Definition cite.cpp:559
static CitationManager & instance()
Definition cite.cpp:80
void clear()
clears the database
Definition cite.cpp:106
QCString replaceFormulas(const QCString &s)
Definition cite.cpp:307
void insert(const QCString &label)
Insert a citation identified by label into the database.
Definition cite.cpp:90
CitationManager()
Create the database, with an expected maximum of size entries.
Definition cite.cpp:86
QCString fileName() const
Definition cite.cpp:117
bool isEmpty() const
return TRUE if there are no citations.
Definition cite.cpp:111
void generatePage()
Generate the citations page.
Definition cite.cpp:327
QCString getFormulas(const QCString &s)
Definition cite.cpp:232
void setText(const QCString &s)
Definition cite.cpp:66
QCString m_label
Definition cite.cpp:69
QCString label() const override
Definition cite.cpp:63
QCString text() const override
Definition cite.cpp:64
QCString m_text
Definition cite.cpp:70
CiteInfoImpl(const QCString &label, const QCString &text=QCString())
Definition cite.cpp:60
QCString fileName
Definition cite.cpp:46
CitePosition(const QCString &fn, int l)
Definition cite.cpp:44
int lineNr
Definition cite.cpp:47
bool parseCommentBlock(OutlineParserInterface *parser, Entry *curEntry, const QCString &comment, const QCString &fileName, int &lineNr, bool isBrief, bool isJavadocStyle, bool isInbody, Protection &prot, int &position, bool &newEntryNeeded, bool markdownEnabled, GuardedSectionStack *guards)
Invokes the comment block parser with the request to parse a single comment block.
@ Cite
Definition debug.h:40
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:135
static std::string currentDirPath()
Definition dir.cpp:340
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
bool rmdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:309
static bool setCurrent(const std::string &path)
Definition dir.cpp:348
bool exists() const
Definition dir.cpp:257
QCString doc
documentation block (partly parsed)
Definition entry.h:200
bool exists() const
Definition fileinfo.cpp:30
void addChar(char c)
Definition growbuf.h:69
void addStr(const QCString &s)
Definition growbuf.h:72
void clear()
Definition growbuf.h:68
char * get()
Definition growbuf.h:114
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
bool startsWith(const char *s) const
Definition qcstring.h:492
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
QCString lower() const
Definition qcstring.h:234
bool endsWith(const char *s) const
Definition qcstring.h:504
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:245
const std::string & str() const
Definition qcstring.h:526
QCString & setNum(short n)
Definition qcstring.h:444
QCString right(size_t len) const
Definition qcstring.h:219
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:159
QCString left(size_t len) const
Definition qcstring.h:214
static ResourceMgr & instance()
Returns the one and only instance of this class.
bool copyResource(const QCString &name, const QCString &targetDir) const
Copies a registered resource to a given target directory.
virtual QCString trCiteReferences()=0
Interface for the comment block scanner.
std::stack< GuardedSection > GuardedSectionStack
Definition commentscan.h:48
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
std::vector< std::string > StringVector
Definition containers.h:33
#define lineCount(s, len)
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:315
Translator * theTranslator
Definition language.cpp:71
#define warn(file, line, fmt,...)
Definition message.h:59
#define err(fmt,...)
Definition message.h:84
std::ifstream openInputStream(const QCString &name, bool binary=false, bool openAtEnd=false)
Definition portable.cpp:676
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:665
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:106
Portable versions of functions that are platform dependent.
#define qsnprintf
Definition qcstring.h:49
const char * qPrint(const char *s)
Definition qcstring.h:661
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
std::map< std::string, std::unique_ptr< CiteInfoImpl > > entries
Definition cite.cpp:75
std::unordered_map< std::string, CitePosition > citePosition
Definition cite.cpp:77
std::unordered_map< int, std::string > formulaCite
Definition cite.cpp:76
Citation-related data.
Definition cite.h:27
Protection
Protection level of members.
Definition types.h:26
@ Public
Definition types.h:26
bool copyFile(const QCString &src, const QCString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:6169
A bunch of utility functions.