]> git.lyx.org Git - lyx.git/blob - src/BiblioInfo.cpp
59ecd7fd3920d4341bf1db78b1001c6d4cbb4d70
[lyx.git] / src / BiblioInfo.cpp
1 /**
2  * \file BiblioInfo.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Herbert Voß
8  * \author Richard Heck
9  * \author Julien Rioux
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "BiblioInfo.h"
17 #include "Buffer.h"
18 #include "BufferParams.h"
19 #include "buffer_funcs.h"
20 #include "Citation.h"
21 #include "Encoding.h"
22 #include "InsetIterator.h"
23 #include "Language.h"
24 #include "output_xhtml.h"
25 #include "Paragraph.h"
26 #include "TextClass.h"
27 #include "TocBackend.h"
28
29 #include "support/convert.h"
30 #include "support/debug.h"
31 #include "support/docstream.h"
32 #include "support/gettext.h"
33 #include "support/lassert.h"
34 #include "support/lstrings.h"
35 #include "support/regex.h"
36 #include "support/textutils.h"
37
38 #include <set>
39
40 using namespace std;
41 using namespace lyx::support;
42
43
44 namespace lyx {
45
46 namespace {
47
48 // gets the "family name" from an author-type string
49 docstring familyName(docstring const & name)
50 {
51         if (name.empty())
52                 return docstring();
53
54         // first we look for a comma, and take the last name to be everything
55         // preceding the right-most one, so that we also get the "jr" part.
56         docstring::size_type idx = name.rfind(',');
57         if (idx != docstring::npos)
58                 return ltrim(name.substr(0, idx));
59
60         // OK, so now we want to look for the last name. We're going to
61         // include the "von" part. This isn't perfect.
62         // Split on spaces, to get various tokens.
63         vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
64         // If we only get two, assume the last one is the last name
65         if (pieces.size() <= 2)
66                 return pieces.back();
67
68         // Now we look for the first token that begins with a lower case letter.
69         vector<docstring>::const_iterator it = pieces.begin();
70         vector<docstring>::const_iterator en = pieces.end();
71         for (; it != en; ++it) {
72                 if ((*it).empty())
73                         continue;
74                 char_type const c = (*it)[0];
75                 if (isLower(c))
76                         break;
77         }
78
79         if (it == en) // we never found a "von"
80                 return pieces.back();
81
82         // reconstruct what we need to return
83         docstring retval;
84         bool first = true;
85         for (; it != en; ++it) {
86                 if (!first)
87                         retval += " ";
88                 else
89                         first = false;
90                 retval += *it;
91         }
92         return retval;
93 }
94
95
96 // converts a string containing LaTeX commands into unicode
97 // for display.
98 docstring convertLaTeXCommands(docstring const & str)
99 {
100         docstring val = str;
101         docstring ret;
102
103         bool scanning_cmd = false;
104         bool scanning_math = false;
105         bool escaped = false; // used to catch \$, etc.
106         while (!val.empty()) {
107                 char_type const ch = val[0];
108
109                 // if we're scanning math, we output everything until we
110                 // find an unescaped $, at which point we break out.
111                 if (scanning_math) {
112                         if (escaped)
113                                 escaped = false;
114                         else if (ch == '\\')
115                                 escaped = true;
116                         else if (ch == '$')
117                                 scanning_math = false;
118                         ret += ch;
119                         val = val.substr(1);
120                         continue;
121                 }
122
123                 // if we're scanning a command name, then we just
124                 // discard characters until we hit something that
125                 // isn't alpha.
126                 if (scanning_cmd) {
127                         if (isAlphaASCII(ch)) {
128                                 val = val.substr(1);
129                                 escaped = false;
130                                 continue;
131                         }
132                         // so we're done with this command.
133                         // now we fall through and check this character.
134                         scanning_cmd = false;
135                 }
136
137                 // was the last character a \? If so, then this is something like:
138                 // \\ or \$, so we'll just output it. That's probably not always right...
139                 if (escaped) {
140                         // exception: output \, as THIN SPACE
141                         if (ch == ',')
142                                 ret.push_back(0x2009);
143                         else
144                                 ret += ch;
145                         val = val.substr(1);
146                         escaped = false;
147                         continue;
148                 }
149
150                 if (ch == '$') {
151                         ret += ch;
152                         val = val.substr(1);
153                         scanning_math = true;
154                         continue;
155                 }
156
157                 // we just ignore braces
158                 if (ch == '{' || ch == '}') {
159                         val = val.substr(1);
160                         continue;
161                 }
162
163                 // we're going to check things that look like commands, so if
164                 // this doesn't, just output it.
165                 if (ch != '\\') {
166                         ret += ch;
167                         val = val.substr(1);
168                         continue;
169                 }
170
171                 // ok, could be a command of some sort
172                 // let's see if it corresponds to some unicode
173                 // unicodesymbols has things in the form: \"{u},
174                 // whereas we may see things like: \"u. So we'll
175                 // look for that and change it, if necessary.
176                 // FIXME: This is a sort of mini-tex2lyx.
177                 //        Use the real tex2lyx instead!
178                 static lyx::regex const reg("^\\\\\\W\\w");
179                 if (lyx::regex_search(to_utf8(val), reg)) {
180                         val.insert(3, from_ascii("}"));
181                         val.insert(2, from_ascii("{"));
182                 }
183                 bool termination;
184                 docstring rem;
185                 docstring const cnvtd = Encodings::fromLaTeXCommand(val,
186                                 Encodings::TEXT_CMD, termination, rem);
187                 if (!cnvtd.empty()) {
188                         // it did, so we'll take that bit and proceed with what's left
189                         ret += cnvtd;
190                         val = rem;
191                         continue;
192                 }
193                 // it's a command of some sort
194                 scanning_cmd = true;
195                 escaped = true;
196                 val = val.substr(1);
197         }
198         return ret;
199 }
200
201
202 // Escape '<' and '>' and remove richtext markers (e.g. {!this is richtext!}) from a string.
203 docstring processRichtext(docstring const & str, bool richtext)
204 {
205         docstring val = str;
206         docstring ret;
207
208         bool scanning_rich = false;
209         while (!val.empty()) {
210                 char_type const ch = val[0];
211                 if (ch == '{' && val.size() > 1 && val[1] == '!') {
212                         // beginning of rich text
213                         scanning_rich = true;
214                         val = val.substr(2);
215                         continue;
216                 }
217                 if (scanning_rich && ch == '!' && val.size() > 1 && val[1] == '}') {
218                         // end of rich text
219                         scanning_rich = false;
220                         val = val.substr(2);
221                         continue;
222                 }
223                 if (richtext) {
224                         if (scanning_rich)
225                                 ret += ch;
226                         else {
227                                 // we need to escape '<' and '>'
228                                 if (ch == '<')
229                                         ret += "&lt;";
230                                 else if (ch == '>')
231                                         ret += "&gt;";
232                                 else
233                                         ret += ch;
234                         }
235                 } else if (!scanning_rich /* && !richtext */)
236                         ret += ch;
237                 // else the character is discarded, which will happen only if
238                 // richtext == false and we are scanning rich text
239                 val = val.substr(1);
240         }
241         return ret;
242 }
243
244 } // anon namespace
245
246
247 //////////////////////////////////////////////////////////////////////
248 //
249 // BibTeXInfo
250 //
251 //////////////////////////////////////////////////////////////////////
252
253 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
254         : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
255           modifier_(0)
256 {}
257
258
259 docstring const BibTeXInfo::getAuthorList(
260     Buffer const * buf, bool full, bool forceshort) const
261 {
262         // Maxnames treshold depend on engine
263         size_t maxnames = buf ?
264                 buf->params().documentClass().max_citenames() : 2;
265
266         if (!is_bibtex_) {
267                 docstring const opt = label();
268                 if (opt.empty())
269                         return docstring();
270
271                 docstring authors;
272                 docstring const remainder = trim(split(opt, authors, '('));
273                 if (remainder.empty())
274                         // in this case, we didn't find a "(",
275                         // so we don't have author (year)
276                         return docstring();
277                 return authors;
278         }
279
280         docstring author = operator[]("author");
281         if (author.empty()) {
282                 author = operator[]("editor");
283                 if (author.empty())
284                         return author;
285         }
286
287         // FIXME Move this to a separate routine that can
288         // be called from elsewhere.
289         //
290         // OK, we've got some names. Let's format them.
291         // Try to split the author list on " and "
292         vector<docstring> const authors =
293                 getVectorFromString(author, from_ascii(" and "));
294
295         docstring retval;
296
297         CiteEngineType const engine_type = buf ? buf->params().citeEngineType()
298                                                : ENGINE_TYPE_DEFAULT;
299
300         // These are defined in the styles
301         string const etal =
302                 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_etal")
303                     : " et al.";
304         string const namesep =
305                 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_namesep")
306                    : ", ";
307         string const lastnamesep =
308                 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_lastnamesep")
309                     : ", and ";
310         string const pairnamesep =
311                 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_pairnamesep")
312                      : " and ";
313
314         // Shorten the list (with et al.) if forceshort is set
315         // and the list can actually be shorten, else if maxcitenames
316         // is passed and full is not set.
317         bool shorten = forceshort && authors.size() > 1;
318         vector<docstring>::const_iterator it = authors.begin();
319         vector<docstring>::const_iterator en = authors.end();
320         for (size_t i = 0; it != en; ++it, ++i) {
321                 if (i >= maxnames && !full) {
322                         shorten = true;
323                         break;
324                 }
325                 if (*it == "others") {
326                         retval += buf ? buf->B_(etal) : from_ascii(etal);
327                         break;
328                 }
329                 if (i > 0 && i == authors.size() - 1) {
330                         if (authors.size() == 2)
331                                 retval += buf ? buf->B_(pairnamesep) : from_ascii(pairnamesep);
332                         else
333                                 retval += buf ? buf->B_(lastnamesep) : from_ascii(lastnamesep);
334                 } else if (i > 0)
335                         retval += buf ? buf->B_(namesep) : from_ascii(namesep);
336                 retval += familyName(*it);
337         }
338         if (shorten)
339                 retval = familyName(authors[0]) + (buf ? buf->B_(etal) : from_ascii(etal));
340
341         return convertLaTeXCommands(retval);
342 }
343
344
345 docstring const BibTeXInfo::getYear() const
346 {
347         if (is_bibtex_) {
348                 // first try legacy year field
349                 docstring year = operator[]("year");
350                 if (!year.empty())
351                         return year;
352                 // now try biblatex's date field
353                 year = operator[]("date");
354                 // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD*
355                 // We only want the years.
356                 static regex const yreg("[-]?([\\d]{4}).*");
357                 static regex const ereg(".*/[-]?([\\d]{4}).*");
358                 smatch sm;
359                 string const date = to_utf8(year);
360                 regex_match(date, sm, yreg);
361                 year = from_ascii(sm[1]);
362                 // check for an endyear
363                 if (regex_match(date, sm, ereg))
364                         year += char_type(0x2013) + from_ascii(sm[1]);
365                 return year;
366         }
367
368         docstring const opt = label();
369         if (opt.empty())
370                 return docstring();
371
372         docstring authors;
373         docstring tmp = split(opt, authors, '(');
374         if (tmp.empty())
375                 // we don't have author (year)
376                 return docstring();
377         docstring year;
378         tmp = split(tmp, year, ')');
379         return year;
380 }
381
382
383 namespace {
384
385 docstring parseOptions(docstring const & format, string & optkey,
386                     docstring & ifpart, docstring & elsepart);
387
388 // Calls parseOptions to deal with an embedded option, such as:
389 //   {%number%[[, no.~%number%]]}
390 // which must appear at the start of format. ifelsepart gets the
391 // whole of the option, and we return what's left after the option.
392 // we return format if there is an error.
393 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
394 {
395         LASSERT(format[0] == '{' && format[1] == '%', return format);
396         string optkey;
397         docstring ifpart;
398         docstring elsepart;
399         docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
400         if (format == rest) { // parse error
401                 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
402                 return format;
403         }
404         LASSERT(rest.size() <= format.size(),
405                 { ifelsepart = docstring(); return format; });
406         ifelsepart = format.substr(0, format.size() - rest.size());
407         return rest;
408 }
409
410
411 // Gets a "clause" from a format string, where the clause is
412 // delimited by '[[' and ']]'. Returns what is left after the
413 // clause is removed, and returns format if there is an error.
414 docstring getClause(docstring const & format, docstring & clause)
415 {
416         docstring fmt = format;
417         // remove '[['
418         fmt = fmt.substr(2);
419         // we'll remove characters from the front of fmt as we
420         // deal with them
421         while (!fmt.empty()) {
422                 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
423                         // that's the end
424                         fmt = fmt.substr(2);
425                         break;
426                 }
427                 // check for an embedded option
428                 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
429                         docstring part;
430                         docstring const rest = parseEmbeddedOption(fmt, part);
431                         if (fmt == rest) {
432                                 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
433                                 return format;
434                         }
435                         clause += part;
436                         fmt = rest;
437                 } else { // it's just a normal character
438                                 clause += fmt[0];
439                                 fmt = fmt.substr(1);
440                 }
441         }
442         return fmt;
443 }
444
445
446 // parse an options string, which must appear at the start of the
447 // format parameter. puts the parsed bits in optkey, ifpart, and
448 // elsepart and returns what's left after the option is removed.
449 // if there's an error, it returns format itself.
450 docstring parseOptions(docstring const & format, string & optkey,
451                     docstring & ifpart, docstring & elsepart)
452 {
453         LASSERT(format[0] == '{' && format[1] == '%', return format);
454         // strip '{%'
455         docstring fmt = format.substr(2);
456         size_t pos = fmt.find('%'); // end of key
457         if (pos == string::npos) {
458                 LYXERR0("Error parsing  `" << format <<"'. Can't find end of key.");
459                 return format;
460         }
461         optkey = to_utf8(fmt.substr(0, pos));
462         fmt = fmt.substr(pos + 1);
463         // [[format]] should be next
464         if (fmt[0] != '[' || fmt[1] != '[') {
465                 LYXERR0("Error parsing  `" << format <<"'. Can't find '[[' after key.");
466                 return format;
467         }
468
469         docstring curfmt = fmt;
470         fmt = getClause(curfmt, ifpart);
471         if (fmt == curfmt) {
472                 LYXERR0("Error parsing  `" << format <<"'. Couldn't get if clause.");
473                 return format;
474         }
475
476         if (fmt[0] == '}') // we're done, no else clause
477                 return fmt.substr(1);
478
479         // else part should follow
480         if (fmt[0] != '[' || fmt[1] != '[') {
481                 LYXERR0("Error parsing  `" << format <<"'. Can't find else clause.");
482                 return format;
483         }
484
485         curfmt = fmt;
486         fmt = getClause(curfmt, elsepart);
487         // we should be done
488         if (fmt == curfmt || fmt[0] != '}') {
489                 LYXERR0("Error parsing  `" << format <<"'. Can't find end of option.");
490                 return format;
491         }
492         return fmt.substr(1);
493 }
494
495
496 } // anon namespace
497
498 /* FIXME
499 Bug #9131 revealed an oddity in how we are generating citation information
500 when more than one key is given. We end up building a longer and longer format 
501 string as we go, which we then have to re-parse, over and over and over again,
502 rather than generating the information for the individual keys and then putting
503 all of that together. We do that to deal with the way separators work, from what
504 I can tell, but it still feels like a hack. Fixing this would require quite a
505 bit of work, however.
506 */
507 docstring BibTeXInfo::expandFormat(docstring const & format,
508                 BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
509                 CiteItem const & ci, bool next, bool second) const
510 {
511         // incorrect use of macros could put us in an infinite loop
512         static int const max_passes = 5000;
513         // the use of overly large keys can lead to performance problems, due
514         // to eventual attempts to convert LaTeX macros to unicode. See bug
515         // #8944. This is perhaps not the best solution, but it will have to
516         // do for now.
517         static size_t const max_keysize = 128;
518         odocstringstream ret; // return value
519         string key;
520         bool scanning_key = false;
521         bool scanning_rich = false;
522
523         CiteEngineType const engine_type = buf.params().citeEngineType();
524         docstring fmt = format;
525         // we'll remove characters from the front of fmt as we
526         // deal with them
527         while (!fmt.empty()) {
528                 if (counter > max_passes) {
529                         LYXERR0("Recursion limit reached while parsing `"
530                                 << format << "'.");
531                         return _("ERROR!");
532                 }
533
534                 char_type thischar = fmt[0];
535                 if (thischar == '%') {
536                         // beginning or end of key
537                         if (scanning_key) {
538                                 // end of key
539                                 scanning_key = false;
540                                 // so we replace the key with its value, which may be empty
541                                 if (key[0] == '!') {
542                                         // macro
543                                         string const val =
544                                                 buf.params().documentClass().getCiteMacro(engine_type, key);
545                                         fmt = from_utf8(val) + fmt.substr(1);
546                                         counter += 1;
547                                         continue;
548                                 } else if (key[0] == '_') {
549                                         // a translatable bit
550                                         string const val =
551                                                 buf.params().documentClass().getCiteMacro(engine_type, key);
552                                         docstring const trans =
553                                                 translateIfPossible(from_utf8(val), buf.params().language->code());
554                                         ret << trans;
555                                 } else {
556                                         docstring const val =
557                                                 getValueForKey(key, buf, ci, xrefs, max_keysize);
558                                         if (!scanning_rich)
559                                                 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
560                                         ret << val;
561                                         if (!scanning_rich)
562                                                 ret << from_ascii("{!</span>!}");
563                                 }
564                         } else {
565                                 // beginning of key
566                                 key.clear();
567                                 scanning_key = true;
568                         }
569                 }
570                 else if (thischar == '{') {
571                         // beginning of option?
572                         if (scanning_key) {
573                                 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
574                                 return _("ERROR!");
575                         }
576                         if (fmt.size() > 1) {
577                                 if (fmt[1] == '%') {
578                                         // it is the beginning of an optional format
579                                         string optkey;
580                                         docstring ifpart;
581                                         docstring elsepart;
582                                         docstring const newfmt =
583                                                 parseOptions(fmt, optkey, ifpart, elsepart);
584                                         if (newfmt == fmt) // parse error
585                                                 return _("ERROR!");
586                                         fmt = newfmt;
587                                         docstring const val =
588                                                 getValueForKey(optkey, buf, ci, xrefs);
589                                         if (optkey == "next" && next)
590                                                 ret << ifpart; // without expansion
591                                         else if (!val.empty()) {
592                                                 int newcounter = 0;
593                                                 ret << expandFormat(ifpart, xrefs, newcounter, buf,
594                                                         ci, next);
595                                         } else if (!elsepart.empty()) {
596                                                 int newcounter = 0;
597                                                 ret << expandFormat(elsepart, xrefs, newcounter, buf,
598                                                         ci, next);
599                                         }
600                                         // fmt will have been shortened for us already
601                                         continue;
602                                 }
603                                 if (fmt[1] == '!') {
604                                         // beginning of rich text
605                                         scanning_rich = true;
606                                         fmt = fmt.substr(2);
607                                         ret << from_ascii("{!");
608                                         continue;
609                                 }
610                         }
611                         // we are here if '{' was not followed by % or !.
612                         // So it's just a character.
613                         ret << thischar;
614                 }
615                 else if (scanning_rich && thischar == '!'
616                          && fmt.size() > 1 && fmt[1] == '}') {
617                         // end of rich text
618                         scanning_rich = false;
619                         fmt = fmt.substr(2);
620                         ret << from_ascii("!}");
621                         continue;
622                 }
623                 else if (scanning_key)
624                         key += char(thischar);
625                 else {
626                         try {
627                                 ret.put(thischar);
628                         } catch (EncodingException & /* e */) {
629                                 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
630                         }
631                 }
632                 fmt = fmt.substr(1);
633         } // for loop
634         if (scanning_key) {
635                 LYXERR0("Never found end of key in `" << format << "'!");
636                 return _("ERROR!");
637         }
638         if (scanning_rich) {
639                 LYXERR0("Never found end of rich text in `" << format << "'!");
640                 return _("ERROR!");
641         }
642         return ret.str();
643 }
644
645
646 docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
647         Buffer const & buf, CiteItem const & ci) const
648 {
649         bool const richtext = ci.richtext;
650
651         if (!richtext && !info_.empty())
652                 return info_;
653         if (richtext && !info_richtext_.empty())
654                 return info_richtext_;
655
656         if (!is_bibtex_) {
657                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
658                 info_ = it->second;
659                 return info_;
660         }
661
662         CiteEngineType const engine_type = buf.params().citeEngineType();
663         DocumentClass const & dc = buf.params().documentClass();
664         docstring const & format =
665                 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
666         int counter = 0;
667         info_ = expandFormat(format, xrefs, counter, buf,
668                 ci, false, false);
669
670         if (info_.empty()) {
671                 // this probably shouldn't happen
672                 return info_;
673         }
674
675         if (richtext) {
676                 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
677                 return info_richtext_;
678         }
679
680         info_ = convertLaTeXCommands(processRichtext(info_, false));
681         return info_;
682 }
683
684
685 docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
686         Buffer const & buf, docstring const & format,
687         CiteItem const & ci, bool next, bool second) const
688 {
689         docstring loclabel;
690
691         int counter = 0;
692         loclabel = expandFormat(format, xrefs, counter, buf, ci, next, second);
693
694         if (!loclabel.empty() && !next) {
695                 loclabel = processRichtext(loclabel, ci.richtext);
696                 loclabel = convertLaTeXCommands(loclabel);
697         }
698
699         return loclabel;
700 }
701
702
703 docstring const & BibTeXInfo::operator[](docstring const & field) const
704 {
705         BibTeXInfo::const_iterator it = find(field);
706         if (it != end())
707                 return it->second;
708         static docstring const empty_value = docstring();
709         return empty_value;
710 }
711
712
713 docstring const & BibTeXInfo::operator[](string const & field) const
714 {
715         return operator[](from_ascii(field));
716 }
717
718
719 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
720         CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
721 {
722         // anything less is pointless
723         LASSERT(maxsize >= 16, maxsize = 16);
724         string key = oldkey;
725         bool cleanit = false;
726         if (prefixIs(oldkey, "clean:")) {
727                 key = oldkey.substr(6);
728                 cleanit = true;
729         }
730
731         docstring ret = operator[](key);
732         if (ret.empty() && !xrefs.empty()) {
733                 vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
734                 vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
735                 for (; it != en; ++it) {
736                         if (*it && !(**it)[key].empty()) {
737                                 ret = (**it)[key];
738                                 break;
739                         }
740                 }
741         }
742         if (ret.empty()) {
743                 // some special keys
744                 // FIXME: dialog, textbefore and textafter have nothing to do with this
745                 if (key == "dialog" && ci.context == CiteItem::Dialog)
746                         ret = from_ascii("x"); // any non-empty string will do
747                 else if (key == "ifstar" && ci.Starred)
748                         ret = from_ascii("x"); // any non-empty string will do
749                 else if (key == "entrytype")
750                         ret = entry_type_;
751                 else if (key == "key")
752                         ret = bib_key_;
753                 else if (key == "label")
754                         ret = label_;
755                 else if (key == "modifier" && modifier_ != 0)
756                         ret = modifier_;
757                 else if (key == "numericallabel")
758                         ret = cite_number_;
759                 else if (key == "abbrvauthor") {
760                         // Special key to provide abbreviated author names,
761                         // with respect to maxcitenames.
762                         ret = getAuthorList(&buf, false, false);
763                         if (ci.forceUpperCase && isLowerCase(ret[0]))
764                                 ret[0] = uppercase(ret[0]);
765                 } else if (key == "fullauthor") {
766                         // Return a full author list
767                         ret = getAuthorList(&buf, true, false);
768                         if (ci.forceUpperCase && isLowerCase(ret[0]))
769                                 ret[0] = uppercase(ret[0]);
770                 } else if (key == "forceabbrvauthor") {
771                         // Special key to provide abbreviated author names,
772                         // irrespective of maxcitenames.
773                         ret = getAuthorList(&buf, false, true);
774                         if (ci.forceUpperCase && isLowerCase(ret[0]))
775                                 ret[0] = uppercase(ret[0]);
776                 } else if (key == "bibentry") {
777                         // Special key to provide the full bibliography entry: see getInfo()
778                         CiteEngineType const engine_type = buf.params().citeEngineType();
779                         DocumentClass const & dc = buf.params().documentClass();
780                         docstring const & format =
781                                 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
782                         int counter = 0;
783                         ret = expandFormat(format, xrefs, counter, buf, ci, false, false);
784                 } else if (key == "textbefore")
785                         ret = ci.textBefore;
786                 else if (key == "textafter")
787                         ret = ci.textAfter;
788                 else if (key == "year")
789                         ret = getYear();
790         }
791
792         if (cleanit)
793                 ret = html::cleanAttr(ret);
794
795         // make sure it is not too big
796         support::truncateWithEllipsis(ret, maxsize);
797         return ret;
798 }
799
800
801 //////////////////////////////////////////////////////////////////////
802 //
803 // BiblioInfo
804 //
805 //////////////////////////////////////////////////////////////////////
806
807 namespace {
808
809 // A functor for use with sort, leading to case insensitive sorting
810 class compareNoCase: public binary_function<docstring, docstring, bool>
811 {
812 public:
813         bool operator()(docstring const & s1, docstring const & s2) const {
814                 return compare_no_case(s1, s2) < 0;
815         }
816 };
817
818 } // namespace anon
819
820
821 vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const
822 {
823         vector<docstring> result;
824         if (!data.isBibTeX())
825                 return result;
826         // Legacy crossref field. This is not nestable.
827         if (!nested && !data["crossref"].empty()) {
828                 docstring const xrefkey = data["crossref"];
829                 result.push_back(xrefkey);
830                 // However, check for nested xdatas
831                 BiblioInfo::const_iterator it = find(xrefkey);
832                 if (it != end()) {
833                         BibTeXInfo const & xref = it->second;
834                         vector<docstring> const nxdata = getXRefs(xref, true);
835                         if (!nxdata.empty())
836                                 result.insert(result.end(), nxdata.begin(), nxdata.end());
837                 }
838         }
839         // Biblatex's xdata field. Infinitely nestable.
840         // XData field can consist of a comma-separated list of keys
841         vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
842         if (!xdatakeys.empty()) {
843                 vector<docstring>::const_iterator xit = xdatakeys.begin();
844                 vector<docstring>::const_iterator xen = xdatakeys.end();
845                 for (; xit != xen; ++xit) {
846                         docstring const xdatakey = *xit;
847                         result.push_back(xdatakey);
848                         BiblioInfo::const_iterator it = find(xdatakey);
849                         if (it != end()) {
850                                 BibTeXInfo const & xdata = it->second;
851                                 vector<docstring> const nxdata = getXRefs(xdata, true);
852                                 if (!nxdata.empty())
853                                         result.insert(result.end(), nxdata.begin(), nxdata.end());
854                         }
855                 }
856         }
857         return result;
858 }
859
860
861 vector<docstring> const BiblioInfo::getKeys() const
862 {
863         vector<docstring> bibkeys;
864         BiblioInfo::const_iterator it  = begin();
865         for (; it != end(); ++it)
866                 bibkeys.push_back(it->first);
867         sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
868         return bibkeys;
869 }
870
871
872 vector<docstring> const BiblioInfo::getFields() const
873 {
874         vector<docstring> bibfields;
875         set<docstring>::const_iterator it = field_names_.begin();
876         set<docstring>::const_iterator end = field_names_.end();
877         for (; it != end; ++it)
878                 bibfields.push_back(*it);
879         sort(bibfields.begin(), bibfields.end());
880         return bibfields;
881 }
882
883
884 vector<docstring> const BiblioInfo::getEntries() const
885 {
886         vector<docstring> bibentries;
887         set<docstring>::const_iterator it = entry_types_.begin();
888         set<docstring>::const_iterator end = entry_types_.end();
889         for (; it != end; ++it)
890                 bibentries.push_back(*it);
891         sort(bibentries.begin(), bibentries.end());
892         return bibentries;
893 }
894
895
896 docstring const BiblioInfo::getAuthorList(docstring const & key, Buffer const & buf) const
897 {
898         BiblioInfo::const_iterator it = find(key);
899         if (it == end())
900                 return docstring();
901         BibTeXInfo const & data = it->second;
902         return data.getAuthorList(&buf, false);
903 }
904
905
906 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
907 {
908         BiblioInfo::const_iterator it = find(key);
909         if (it == end())
910                 return docstring();
911         BibTeXInfo const & data = it->second;
912         return data.citeNumber();
913 }
914
915
916 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
917 {
918         BiblioInfo::const_iterator it = find(key);
919         if (it == end())
920                 return docstring();
921         BibTeXInfo const & data = it->second;
922         docstring year = data.getYear();
923         if (year.empty()) {
924                 // let's try the crossrefs
925                 vector<docstring> const xrefs = getXRefs(data);
926                 if (xrefs.empty())
927                         // no luck
928                         return docstring();
929                 vector<docstring>::const_iterator it = xrefs.begin();
930                 vector<docstring>::const_iterator en = xrefs.end();
931                 for (; it != en; ++it) {
932                         BiblioInfo::const_iterator const xrefit = find(*it);
933                         if (xrefit == end())
934                                 continue;
935                         BibTeXInfo const & xref_data = xrefit->second;
936                         year = xref_data.getYear();
937                         if (!year.empty())
938                                 // success!
939                                 break;
940                 }
941         }
942         if (use_modifier && data.modifier() != 0)
943                 year += data.modifier();
944         return year;
945 }
946
947
948 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
949 {
950         docstring const year = getYear(key, use_modifier);
951         if (year.empty())
952                 return buf.B_("No year");
953         return year;
954 }
955
956
957 docstring const BiblioInfo::getInfo(docstring const & key,
958         Buffer const & buf, CiteItem const & ci) const
959 {
960         BiblioInfo::const_iterator it = find(key);
961         if (it == end())
962                 return docstring(_("Bibliography entry not found!"));
963         BibTeXInfo const & data = it->second;
964         BibTeXInfoList xrefptrs;
965         vector<docstring> const xrefs = getXRefs(data);
966         if (!xrefs.empty()) {
967                 vector<docstring>::const_iterator it = xrefs.begin();
968                 vector<docstring>::const_iterator en = xrefs.end();
969                 for (; it != en; ++it) {
970                         BiblioInfo::const_iterator const xrefit = find(*it);
971                         if (xrefit != end())
972                                 xrefptrs.push_back(&(xrefit->second));
973                 }
974         }
975         return data.getInfo(xrefptrs, buf, ci);
976 }
977
978
979 docstring const BiblioInfo::getLabel(vector<docstring> keys,
980         Buffer const & buf, string const & style, CiteItem const & ci) const
981 {
982         size_t max_size = ci.max_size;
983         // shorter makes no sense
984         LASSERT(max_size >= 16, max_size = 16);
985
986         // we can't display more than 10 of these, anyway
987         bool const too_many_keys = keys.size() > 10;
988         if (too_many_keys)
989                 keys.resize(10);
990
991         CiteEngineType const engine_type = buf.params().citeEngineType();
992         DocumentClass const & dc = buf.params().documentClass();
993         docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
994         docstring ret = format;
995         vector<docstring>::const_iterator key = keys.begin();
996         vector<docstring>::const_iterator ken = keys.end();
997         for (; key != ken; ++key) {
998                 BiblioInfo::const_iterator it = find(*key);
999                 BibTeXInfo empty_data;
1000                 empty_data.key(*key);
1001                 BibTeXInfo & data = empty_data;
1002                 vector<BibTeXInfo const *> xrefptrs;
1003                 if (it != end()) {
1004                         data = it->second;
1005                         vector<docstring> const xrefs = getXRefs(data);
1006                         if (!xrefs.empty()) {
1007                                 vector<docstring>::const_iterator it = xrefs.begin();
1008                                 vector<docstring>::const_iterator en = xrefs.end();
1009                                 for (; it != en; ++it) {
1010                                         BiblioInfo::const_iterator const xrefit = find(*it);
1011                                         if (xrefit != end())
1012                                                 xrefptrs.push_back(&(xrefit->second));
1013                                 }
1014                         }
1015                 }
1016                 ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
1017         }
1018
1019         if (too_many_keys)
1020                 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
1021         support::truncateWithEllipsis(ret, max_size);
1022         return ret;
1023 }
1024
1025
1026 bool BiblioInfo::isBibtex(docstring const & key) const
1027 {
1028         docstring key1;
1029         split(key, key1, ',');
1030         BiblioInfo::const_iterator it = find(key1);
1031         if (it == end())
1032                 return false;
1033         return it->second.isBibTeX();
1034 }
1035
1036
1037 vector<docstring> const BiblioInfo::getCiteStrings(
1038         vector<docstring> const & keys, vector<CitationStyle> const & styles,
1039         Buffer const & buf, CiteItem const & ci) const
1040 {
1041         if (empty())
1042                 return vector<docstring>();
1043
1044         string style;
1045         vector<docstring> vec(styles.size());
1046         for (size_t i = 0; i != vec.size(); ++i) {
1047                 style = styles[i].name;
1048                 vec[i] = getLabel(keys, buf, style, ci);
1049         }
1050
1051         return vec;
1052 }
1053
1054
1055 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
1056 {
1057         bimap_.insert(info.begin(), info.end());
1058         field_names_.insert(info.field_names_.begin(), info.field_names_.end());
1059         entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
1060 }
1061
1062
1063 namespace {
1064
1065 // used in xhtml to sort a list of BibTeXInfo objects
1066 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
1067 {
1068         docstring const lauth = lhs->getAuthorList();
1069         docstring const rauth = rhs->getAuthorList();
1070         docstring const lyear = lhs->getYear();
1071         docstring const ryear = rhs->getYear();
1072         docstring const ltitl = lhs->operator[]("title");
1073         docstring const rtitl = rhs->operator[]("title");
1074         return  (lauth < rauth)
1075                 || (lauth == rauth && lyear < ryear)
1076                 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
1077 }
1078
1079 }
1080
1081
1082 void BiblioInfo::collectCitedEntries(Buffer const & buf)
1083 {
1084         cited_entries_.clear();
1085         // We are going to collect all the citation keys used in the document,
1086         // getting them from the TOC.
1087         // FIXME We may want to collect these differently, in the first case,
1088         // so that we might have them in order of appearance.
1089         set<docstring> citekeys;
1090         shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1091         Toc::const_iterator it = toc->begin();
1092         Toc::const_iterator const en = toc->end();
1093         for (; it != en; ++it) {
1094                 if (it->str().empty())
1095                         continue;
1096                 vector<docstring> const keys = getVectorFromString(it->str());
1097                 citekeys.insert(keys.begin(), keys.end());
1098         }
1099         if (citekeys.empty())
1100                 return;
1101
1102         // We have a set of the keys used in this document.
1103         // We will now convert it to a list of the BibTeXInfo objects used in
1104         // this document...
1105         vector<BibTeXInfo const *> bi;
1106         set<docstring>::const_iterator cit = citekeys.begin();
1107         set<docstring>::const_iterator const cen = citekeys.end();
1108         for (; cit != cen; ++cit) {
1109                 BiblioInfo::const_iterator const bt = find(*cit);
1110                 if (bt == end() || !bt->second.isBibTeX())
1111                         continue;
1112                 bi.push_back(&(bt->second));
1113         }
1114         // ...and sort it.
1115         sort(bi.begin(), bi.end(), lSorter);
1116
1117         // Now we can write the sorted keys
1118         vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1119         vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1120         for (; bit != ben; ++bit)
1121                 cited_entries_.push_back((*bit)->key());
1122 }
1123
1124
1125 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1126 {
1127         collectCitedEntries(buf);
1128         CiteEngineType const engine_type = buf.params().citeEngineType();
1129         bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1130
1131         int keynumber = 0;
1132         char modifier = 0;
1133         // used to remember the last one we saw
1134         // we'll be comparing entries to see if we need to add
1135         // modifiers, like "1984a"
1136         map<docstring, BibTeXInfo>::iterator last;
1137
1138         vector<docstring>::const_iterator it = cited_entries_.begin();
1139         vector<docstring>::const_iterator const en = cited_entries_.end();
1140         for (; it != en; ++it) {
1141                 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1142                 // this shouldn't happen, but...
1143                 if (biit == bimap_.end())
1144                         // ...fail gracefully, anyway.
1145                         continue;
1146                 BibTeXInfo & entry = biit->second;
1147                 if (numbers) {
1148                         docstring const num = convert<docstring>(++keynumber);
1149                         entry.setCiteNumber(num);
1150                 } else {
1151                         // coverity complains about our derefercing the iterator last,
1152                         // which was not initialized above. but it does get initialized
1153                         // after the first time through the loop, which is the point of
1154                         // the first test.
1155                         // coverity[FORWARD_NULL]
1156                         if (it != cited_entries_.begin()
1157                             && entry.getAuthorList() == last->second.getAuthorList()
1158                             // we access the year via getYear() so as to get it from the xref,
1159                             // if we need to do so
1160                             && getYear(entry.key()) == getYear(last->second.key())) {
1161                                 if (modifier == 0) {
1162                                         // so the last one should have been 'a'
1163                                         last->second.setModifier('a');
1164                                         modifier = 'b';
1165                                 } else if (modifier == 'z')
1166                                         modifier = 'A';
1167                                 else
1168                                         modifier++;
1169                         } else {
1170                                 modifier = 0;
1171                         }
1172                         entry.setModifier(modifier);
1173                         // remember the last one
1174                         last = biit;
1175                 }
1176         }
1177         // Set the labels
1178         it = cited_entries_.begin();
1179         for (; it != en; ++it) {
1180                 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1181                 // this shouldn't happen, but...
1182                 if (biit == bimap_.end())
1183                         // ...fail gracefully, anyway.
1184                         continue;
1185                 BibTeXInfo & entry = biit->second;
1186                 if (numbers) {
1187                         entry.label(entry.citeNumber());
1188                 } else {
1189                         docstring const auth = entry.getAuthorList(&buf, false);
1190                         // we do it this way so as to access the xref, if necessary
1191                         // note that this also gives us the modifier
1192                         docstring const year = getYear(*it, buf, true);
1193                         if (!auth.empty() && !year.empty())
1194                                 entry.label(auth + ' ' + year);
1195                         else
1196                                 entry.label(entry.key());
1197                 }
1198         }
1199 }
1200
1201
1202 //////////////////////////////////////////////////////////////////////
1203 //
1204 // CitationStyle
1205 //
1206 //////////////////////////////////////////////////////////////////////
1207
1208
1209 CitationStyle citationStyleFromString(string const & command,
1210                                       BufferParams const & params)
1211 {
1212         CitationStyle cs;
1213         if (command.empty())
1214                 return cs;
1215
1216         string const alias = params.getCiteAlias(command);
1217         string cmd = alias.empty() ? command : alias;
1218         if (isUpperCase(command[0])) {
1219                 cs.forceUpperCase = true;
1220                 cmd[0] = lowercase(cmd[0]);
1221         }
1222
1223         size_t const n = command.size() - 1;
1224         if (command[n] == '*') {
1225                 cs.hasStarredVersion = true;
1226                 if (suffixIs(cmd, '*'))
1227                         cmd = cmd.substr(0, cmd.size() - 1);
1228         }
1229
1230         cs.name = cmd;
1231         return cs;
1232 }
1233
1234
1235 string citationStyleToString(const CitationStyle & cs, bool const latex)
1236 {
1237         string cmd = latex ? cs.cmd : cs.name;
1238         if (cs.forceUpperCase)
1239                 cmd[0] = uppercase(cmd[0]);
1240         if (cs.hasStarredVersion)
1241                 cmd += '*';
1242         return cmd;
1243 }
1244
1245 } // namespace lyx