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
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:
62
63 QCString label() const override { return m_label; }
64 QCString text() const override { return m_text; }
65 QCString shortAuthor() const override { return m_shortAuthor; }
66 QCString year() const override { return m_year; }
67
68 void setText(const QCString &s) { m_text = s; }
69 void setShortAuthor(const QCString &s) { m_shortAuthor = s; }
70 void setYear(const QCString &s) { m_year = s; }
71
72 private:
77};
78
80{
81 std::map< std::string,std::unique_ptr<CiteInfoImpl> > entries;
82 std::unordered_map< int,std::string > formulaCite;
83 std::unordered_map< std::string, CitePosition > citePosition;
84};
85
87{
88 static CitationManager ct;
89 return ct;
90}
91
95
97{
98 QCString lowerCaseLabel = label.lower();
99 p->entries.emplace(lowerCaseLabel.str(),std::make_unique<CiteInfoImpl>(lowerCaseLabel));
100}
101
102const CiteInfo *CitationManager::find(const QCString &label) const
103{
104 if (auto it = p->entries.find(label.lower().str()); it != p->entries.end())
105 {
106 return it->second.get();
107 }
108 return nullptr;
109}
110
112{
113 p->entries.clear();
114}
115
117{
118 size_t numFiles = Config_getList(CITE_BIB_FILES).size();
119 return (numFiles==0 || p->entries.empty());
120}
121
123{
124 return "citelist";
125}
126
128{
129 return "CITEREF_";
130}
131
133{
134 // sanity checks
135 if (bibFile.isEmpty())
136 {
137 return;
138 }
139 FileInfo fi(bibFile.str());
140 if (!fi.exists())
141 {
142 err("bib file {} not found!\n",bibFile);
143 return;
144 }
145 std::ifstream f = Portable::openInputStream(bibFile);
146 if (!f.is_open())
147 {
148 err("could not open file {} for reading\n",bibFile);
149 return;
150 }
151
152 // search for citation cross references
153 QCString citeName;
154
155 std::string lineStr;
156 int lineCount = 0;
157 while (getline(f,lineStr))
158 {
159 int i = -1;
160 QCString line(lineStr);
161 lineCount++;
162 if (line.stripWhiteSpace().startsWith("@"))
163 {
164 // assumption entry like: "@book { name," or "@book { name" (spaces optional)
165 int j = line.find('{');
166 // when no {, go hunting for it
167 while (j==-1 && getline(f,lineStr))
168 {
169 line = lineStr;
170 lineCount++;
171 j = line.find('{');
172 }
173 // search for the name
174 citeName = "";
175 if (!f.eof() && j!=-1) // to prevent something like "@manual ," and no { found
176 {
177 int k = line.find(',',j);
178 j++;
179 // found a line "@....{.....,...." or "@.....{....."
180 // ^=j ^=k ^=j k=-1
181 while (!f.eof() && citeName.isEmpty())
182 {
183 if (k!=-1)
184 {
185 citeName = line.mid(static_cast<size_t>(j),static_cast<size_t>(k-j));
186 }
187 else
188 {
189 citeName = line.mid(static_cast<size_t>(j));
190 }
191 citeName = citeName.stripWhiteSpace();
192 j = 0;
193 if (citeName.isEmpty() && getline(f,lineStr))
194 {
195 line = lineStr;
196 lineCount++;
197 k = line.find(',');
198 }
199 }
200 }
201 //printf("citeName = #%s#\n",qPrint(citeName));
202 if (!citeName.isEmpty())
203 {
204 std::string lCiteName = citeName.lower().str();
205 auto it = p->citePosition.find(lCiteName);
206 if (it != p->citePosition.end())
207 {
208 warn(bibFile,lineCount,"multiple use of citation name '{}', (first occurrence: {}, line {})",
209 lCiteName,it->second.fileName,it->second.lineNr);
210 }
211 else
212 {
213 p->citePosition.emplace(lCiteName,CitePosition(bibFile,lineCount));
214 }
215 }
216 }
217 else if ((i=line.find("crossref"))!=-1 && !citeName.isEmpty()) /* assumption cross reference is on one line and the only item */
218 {
219 int j = line.find('{',i);
220 int k = line.find('}',i);
221 if (j>i && k>j)
222 {
223 QCString crossrefName = line.mid(static_cast<size_t>(j+1),static_cast<uint32_t>(k-j-1));
224 // check if the reference with the cross reference is used
225 // insert cross reference when cross reference has not yet been added.
226 if (find(citeName) && !find(crossrefName)) // not found yet
227 {
228 insert(crossrefName);
229 }
230 }
231 }
232 }
233}
234
235const std::string g_formulaMarker = "CITE_FORMULA_";
236
238{
239 if (s.isEmpty()) return s;
240 GrowBuf growBuf;
241 GrowBuf formulaBuf;
242 bool insideFormula = false;
243 int citeFormulaCnt = 1;
244 const size_t tmpLen = 30;
245 char tmp[tmpLen];
246 const char *ps=s.data();
247 char c = 0;
248 while ((c=*ps++))
249 {
250 if (insideFormula)
251 {
252 switch (c)
253 {
254 case '\\':
255 formulaBuf.addChar(c);
256 c = *ps++;
257 formulaBuf.addChar(c);
258 break;
259 case '\n':
260 formulaBuf.addChar(c);
261 formulaBuf.addChar(0);
262 growBuf.addChar('$');
263 growBuf.addStr(formulaBuf.get());
264 insideFormula = false;
265 formulaBuf.clear();
266 break;
267 case '$':
268 qsnprintf(tmp,tmpLen,"%s%06d",g_formulaMarker.c_str(),citeFormulaCnt);
269 formulaBuf.addChar(0);
270 p->formulaCite.emplace(citeFormulaCnt,std::string("\\f$") + formulaBuf.get() + "\\f$");
271 citeFormulaCnt++;
272 // need { and } due to the capitalization rules of bibtex.
273 growBuf.addChar('{');
274 growBuf.addStr(tmp);
275 growBuf.addChar('}');
276 insideFormula = false;
277 formulaBuf.clear();
278 break;
279 default:
280 formulaBuf.addChar(c);
281 break;
282 }
283 }
284 else
285 {
286 switch (c)
287 {
288 case '\\':
289 growBuf.addChar(c);
290 c = *ps++;
291 growBuf.addChar(c);
292 break;
293 case '$':
294 insideFormula = true;
295 break;
296 default:
297 growBuf.addChar(c);
298 break;
299 }
300 }
301 }
302 if (insideFormula)
303 {
304 formulaBuf.addChar(0);
305 growBuf.addStr(formulaBuf.get());
306 formulaBuf.clear();
307 }
308 growBuf.addChar(0);
309 return growBuf.get();
310}
311
313{
314 if (s.isEmpty()) return s;
315 QCString t;
316 int pos=0;
317 int i = -1;
318 while ((i=s.find(g_formulaMarker,pos))!=-1)
319 {
320 t += s.mid(pos,i-pos);
321 int markerSize = static_cast<int>( g_formulaMarker.length());
322 int markerId = atoi(s.mid(i+markerSize,6).data());
323 auto it = p->formulaCite.find(markerId);
324 if (it != p->formulaCite.end()) t += it->second;
325 pos = i + markerSize+6;
326 }
327 t += s.mid(pos);
328 //printf("replaceFormulas(%s)=%s\n",qPrint(s),qPrint(t));
329 return t;
330}
331
333{
334 //printf("** CitationManager::generatePage() count=%d\n",m_ordering.count());
335
336 // do not generate an empty citations page
337 if (isEmpty()) return; // nothing to cite
338
339 bool citeDebug = Debug::isFlagSet(Debug::Cite);
340
341 // 0. add cross references from the bib files to the cite dictionary
342 const StringVector &citeDataList = Config_getList(CITE_BIB_FILES);
343 for (const auto &bibdata : citeDataList)
344 {
345 QCString bibFile = getBibFile(bibdata);
347 }
348
349 // 1. generate file with markers and citations to OUTPUT_DIRECTORY
350 QCString outputDir = Config_getString(OUTPUT_DIRECTORY);
351 QCString citeListFile = outputDir+"/citelist.doc";
352 {
353 std::ofstream t = Portable::openOutputStream(citeListFile);
354 if (!t.is_open())
355 {
356 err("could not open file {} for writing\n",citeListFile);
357 }
358 t << "<!-- BEGIN CITATIONS -->\n";
359 t << "<!--\n";
360 for (const auto &it : p->entries)
361 {
362 t << "\\citation{" << it.second->label() << "}\n";
363 }
364 t << "-->\n";
365 t << "<!-- END CITATIONS -->\n";
366 t << "<!-- BEGIN BIBLIOGRAPHY -->\n";
367 t << "<!-- END BIBLIOGRAPHY -->\n";
368 t.close();
369 }
370
371 // 2. generate bib2xhtml
372 QCString bib2xhtmlFile = outputDir+"/bib2xhtml.pl";
373 ResourceMgr::instance().copyResource("bib2xhtml.pl",outputDir);
374
375 // 3. generate doxygen.bst
376 QCString doxygenBstFile = outputDir+"/doxygen.bst";
377 ResourceMgr::instance().copyResource("doxygen.bst",outputDir);
378
379 // 4. for all formats we just copy the bib files to as special output directory
380 // so bibtex can find them without path (bibtex doesn't support paths or
381 // filenames with spaces!)
382 // Strictly not required when only latex is generated
383 QCString bibOutputDir = outputDir+"/"+bibTmpDir;
384 QCString bibOutputFiles = "";
385 Dir thisDir;
386 if (!thisDir.exists(bibOutputDir.str()) && !thisDir.mkdir(bibOutputDir.str()))
387 {
388 err("Failed to create temporary output directory '{}', skipping citations\n",bibOutputDir);
389 return;
390 }
391 int i = 0;
392 for (const auto &bibdata : citeDataList)
393 {
394 QCString bibFile = getBibFile(bibdata);
395 FileInfo fi(bibFile.str());
396 if (fi.exists())
397 {
398 if (!bibFile.isEmpty())
399 {
400 ++i;
401 std::ifstream f_org = Portable::openInputStream(bibFile);
402 if (!f_org.is_open())
403 {
404 err("could not open file {} for reading\n",bibFile);
405 }
406 std::ofstream f_out = Portable::openOutputStream(bibOutputDir + bibTmpFile + QCString().setNum(i) + ".bib");
407 if (!f_out.is_open())
408 {
409 err("could not open file {}{}{:d}{} for reading\n",bibOutputDir,bibTmpFile,i,".bib");
410 }
411 QCString docs;
412 std::string lineStr;
413 while (getline(f_org,lineStr))
414 {
415 docs += lineStr + "\n";
416 }
417 docs = getFormulas(docs);
418 f_out << docs;
419 if (f_org.is_open()) f_org.close();
420 if (f_out.is_open()) f_out.close();
421 bibOutputFiles = bibOutputFiles + " " + bibTmpDir + bibTmpFile + QCString().setNum(i) + ".bib";
422 }
423 }
424 }
425
426 std::string oldDir = Dir::currentDirPath();
427 Dir::setCurrent(outputDir.str());
428
429 // 5. run bib2xhtml perl script on the generated file which will insert the
430 // bibliography in citelist.doc
431 QCString perlArgs = "\""+bib2xhtmlFile+"\" "+bibOutputFiles+" \""+ citeListFile+"\"";
432 if (citeDebug) perlArgs+=" -d";
433 int exitCode = Portable::system("perl",perlArgs);
434 if (exitCode!=0)
435 {
436 err("Problems running bibtex. Verify that the command 'perl --version' works from the command line. Exit code: {}\n",
437 exitCode);
438 }
439
440 Dir::setCurrent(oldDir);
441
442 // 6. read back the file
443 QCString doc;
444 {
445 std::ifstream f = Portable::openInputStream(citeListFile);
446 if (!f.is_open())
447 {
448 err("could not open file {} for reading\n",citeListFile);
449 }
450
451 bool insideBib=FALSE;
452 //printf("input=[%s]\n",qPrint(input));
453 std::string lineStr;
454 while (getline(f,lineStr))
455 {
456 QCString line(lineStr);
457 //printf("pos=%d s=%d line=[%s]\n",pos,s,qPrint(line));
458
459 if (line.find("<!-- BEGIN BIBLIOGRAPHY")!=-1) insideBib=TRUE;
460 else if (line.find("<!-- END BIBLIOGRAPH")!=-1) insideBib=FALSE;
461 // determine text to use at the location of the @cite command
462 if (insideBib && ((i=line.find("name=\"CITEREF_"))!=-1 || (i=line.find("name=\"#CITEREF_"))!=-1))
463 {
464 int j=line.find("\">[");
465 int j1=line.find("<!--[");
466 int k=line.find("]<!--");
467 int k1=line.find("]-->");
468 if (j!=-1 && k!=-1)
469 {
470 size_t ui=static_cast<size_t>(i);
471 size_t uj0=static_cast<size_t>(j);
472 size_t uj=static_cast<size_t>(j1);
473 size_t uk=static_cast<size_t>(k1);
474 QCString label = line.mid(ui+14,uj0-ui-14);
475 StringVector optList = split(line.mid(uj+5,uk-uj-5).str(),",");
476 QCString number = optList[0];
477 QCString shortAuthor = optList[1];
478 QCString year;
479 if (optList.size() == 3)
480 {
481 year = optList[2];
482 }
483 line = line.left(ui+14) + label + line.right(line.length()-uj0);
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
528 addRelatedPage(fileName(),theTranslator->trCiteReferences(),doc,fileName(),1,1);
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 QCString latexOutputDir = Config_getString(LATEX_OUTPUT)+"/";
536 i = 0;
537 for (const auto &bibdata : citeDataList)
538 {
539 QCString bibFile = getBibFile(bibdata);
540 FileInfo fi(bibFile.str());
541 if (fi.exists())
542 {
543 if (!bibFile.isEmpty())
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 + QCString().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 QCString bibFile = bibOutputDir + bibTmpFile + QCString().setNum(static_cast<int>(j)) + ".bib";
570 thisDir.remove(bibFile.str());
571 }
572 thisDir.rmdir(bibOutputDir.str());
573 }
574}
575
577{
578 QCString result;
579 const StringVector &citeDataList = Config_getList(CITE_BIB_FILES);
580 int i = 0;
581 for (const auto &bibdata : citeDataList)
582 {
583 QCString bibFile = getBibFile(bibdata);
584 FileInfo fi(bibFile.str());
585 if (fi.exists() && !bibFile.isEmpty())
586 {
587 if (i) result += ",";
588 i++;
589 result += bibTmpFile;
590 result += QCString().setNum(i);
591 }
592 }
593 return result;
594}
static QCString getBibFile(const QCString &inFile)
Definition cite.cpp:50
const std::string g_formulaMarker
Definition cite.cpp:235
const char * bibTmpFile
Definition cite.cpp:37
const char * bibTmpDir
Definition cite.cpp:38
void insertCrossReferencesForBibFile(const QCString &bibFile)
Definition cite.cpp:132
QCString anchorPrefix() const
Definition cite.cpp:127
std::unique_ptr< Private > p
Definition cite.h:123
const CiteInfo * find(const QCString &label) const
Return the citation info for a given label.
Definition cite.cpp:102
QCString latexBibFiles()
lists the bibtex cite files in a comma separated list
Definition cite.cpp:576
static CitationManager & instance()
Definition cite.cpp:86
void clear()
clears the database
Definition cite.cpp:111
QCString replaceFormulas(const QCString &s)
Definition cite.cpp:312
void insert(const QCString &label)
Insert a citation identified by label into the database.
Definition cite.cpp:96
CitationManager()
Create the database, with an expected maximum of size entries.
Definition cite.cpp:92
QCString fileName() const
Definition cite.cpp:122
bool isEmpty() const
return TRUE if there are no citations.
Definition cite.cpp:116
void generatePage()
Generate the citations page.
Definition cite.cpp:332
QCString getFormulas(const QCString &s)
Definition cite.cpp:237
void setText(const QCString &s)
Definition cite.cpp:68
QCString m_label
Definition cite.cpp:73
QCString label() const override
Definition cite.cpp:63
QCString text() const override
Definition cite.cpp:64
QCString m_shortAuthor
Definition cite.cpp:75
QCString year() const override
Definition cite.cpp:66
QCString m_text
Definition cite.cpp:74
QCString m_year
Definition cite.cpp:76
CiteInfoImpl(const QCString &label, const QCString &text=QCString())
Definition cite.cpp:60
QCString shortAuthor() const override
Definition cite.cpp:65
void setYear(const QCString &s)
Definition cite.cpp:70
void setShortAuthor(const QCString &s)
Definition cite.cpp:69
class that provide information about the p[osition of a citation name
Definition cite.cpp:42
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:41
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
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:350
bool exists() const
Definition dir.cpp:257
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:116
QCString doc
documentation block (partly parsed)
Definition entry.h:200
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
Class representing a string buffer optimized for growing.
Definition growbuf.h:28
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:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
QCString right(size_t len) const
Definition qcstring.h:234
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:172
QCString left(size_t len) const
Definition qcstring.h:229
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.
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:328
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 QCString &name, bool binary=false, bool openAtEnd=false)
Definition portable.cpp:660
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:649
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
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
std::map< std::string, std::unique_ptr< CiteInfoImpl > > entries
Definition cite.cpp:81
std::unordered_map< std::string, CitePosition > citePosition
Definition cite.cpp:83
std::unordered_map< int, std::string > formulaCite
Definition cite.cpp:82
Citation-related data.
Definition cite.h:70
Protection
Definition types.h:32
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6548
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:5789
A bunch of utility functions.