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