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