]> git.lyx.org Git - lyx.git/blob - src/BiblioInfo.cpp
75ea5a217df00428e81c09721c179393036132c2
[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. By default, the size is limited to 128 (in CiteItem), but
516         // for specific purposes (such as XHTML export), it needs to be enlarged
517         // This is perhaps not the best solution, but it will have to do for now.
518         size_t const max_keysize = ci.max_key_size;
519         odocstringstream ret; // return value
520         string key;
521         bool scanning_key = false;
522         bool scanning_rich = false;
523
524         CiteEngineType const engine_type = buf.params().citeEngineType();
525         docstring fmt = format;
526         // we'll remove characters from the front of fmt as we
527         // deal with them
528         while (!fmt.empty()) {
529                 if (counter > max_passes) {
530                         LYXERR0("Recursion limit reached while parsing `"
531                                 << format << "'.");
532                         return _("ERROR!");
533                 }
534
535                 char_type thischar = fmt[0];
536                 if (thischar == '%') {
537                         // beginning or end of key
538                         if (scanning_key) {
539                                 // end of key
540                                 scanning_key = false;
541                                 // so we replace the key with its value, which may be empty
542                                 if (key[0] == '!') {
543                                         // macro
544                                         string const val =
545                                                 buf.params().documentClass().getCiteMacro(engine_type, key);
546                                         fmt = from_utf8(val) + fmt.substr(1);
547                                         counter += 1;
548                                         continue;
549                                 } else if (key[0] == '_') {
550                                         // a translatable bit
551                                         string const val =
552                                                 buf.params().documentClass().getCiteMacro(engine_type, key);
553                                         docstring const trans =
554                                                 translateIfPossible(from_utf8(val), buf.params().language->code());
555                                         ret << trans;
556                                 } else {
557                                         docstring const val =
558                                                 getValueForKey(key, buf, ci, xrefs, max_keysize);
559                                         if (!scanning_rich)
560                                                 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
561                                         ret << val;
562                                         if (!scanning_rich)
563                                                 ret << from_ascii("{!</span>!}");
564                                 }
565                         } else {
566                                 // beginning of key
567                                 key.clear();
568                                 scanning_key = true;
569                         }
570                 }
571                 else if (thischar == '{') {
572                         // beginning of option?
573                         if (scanning_key) {
574                                 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
575                                 return _("ERROR!");
576                         }
577                         if (fmt.size() > 1) {
578                                 if (fmt[1] == '%') {
579                                         // it is the beginning of an optional format
580                                         string optkey;
581                                         docstring ifpart;
582                                         docstring elsepart;
583                                         docstring const newfmt =
584                                                 parseOptions(fmt, optkey, ifpart, elsepart);
585                                         if (newfmt == fmt) // parse error
586                                                 return _("ERROR!");
587                                         fmt = newfmt;
588                                         docstring const val =
589                                                 getValueForKey(optkey, buf, ci, xrefs);
590                                         if (optkey == "next" && next)
591                                                 ret << ifpart; // without expansion
592                                         else if (optkey == "second" && second) {
593                                                 int newcounter = 0;
594                                                 ret << expandFormat(ifpart, xrefs, newcounter, buf,
595                                                         ci, next);
596                                         } else if (!val.empty()) {
597                                                 int newcounter = 0;
598                                                 ret << expandFormat(ifpart, xrefs, newcounter, buf,
599                                                         ci, next);
600                                         } else if (!elsepart.empty()) {
601                                                 int newcounter = 0;
602                                                 ret << expandFormat(elsepart, xrefs, newcounter, buf,
603                                                         ci, next);
604                                         }
605                                         // fmt will have been shortened for us already
606                                         continue;
607                                 }
608                                 if (fmt[1] == '!') {
609                                         // beginning of rich text
610                                         scanning_rich = true;
611                                         fmt = fmt.substr(2);
612                                         ret << from_ascii("{!");
613                                         continue;
614                                 }
615                         }
616                         // we are here if '{' was not followed by % or !.
617                         // So it's just a character.
618                         ret << thischar;
619                 }
620                 else if (scanning_rich && thischar == '!'
621                          && fmt.size() > 1 && fmt[1] == '}') {
622                         // end of rich text
623                         scanning_rich = false;
624                         fmt = fmt.substr(2);
625                         ret << from_ascii("!}");
626                         continue;
627                 }
628                 else if (scanning_key)
629                         key += char(thischar);
630                 else {
631                         try {
632                                 ret.put(thischar);
633                         } catch (EncodingException & /* e */) {
634                                 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
635                         }
636                 }
637                 fmt = fmt.substr(1);
638         } // for loop
639         if (scanning_key) {
640                 LYXERR0("Never found end of key in `" << format << "'!");
641                 return _("ERROR!");
642         }
643         if (scanning_rich) {
644                 LYXERR0("Never found end of rich text in `" << format << "'!");
645                 return _("ERROR!");
646         }
647         return ret.str();
648 }
649
650
651 docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
652         Buffer const & buf, CiteItem const & ci) const
653 {
654         bool const richtext = ci.richtext;
655
656         if (!richtext && !info_.empty())
657                 return info_;
658         if (richtext && !info_richtext_.empty())
659                 return info_richtext_;
660
661         if (!is_bibtex_) {
662                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
663                 info_ = it->second;
664                 return info_;
665         }
666
667         CiteEngineType const engine_type = buf.params().citeEngineType();
668         DocumentClass const & dc = buf.params().documentClass();
669         docstring const & format =
670                 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
671         int counter = 0;
672         info_ = expandFormat(format, xrefs, counter, buf,
673                 ci, false, false);
674
675         if (info_.empty()) {
676                 // this probably shouldn't happen
677                 return info_;
678         }
679
680         if (richtext) {
681                 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
682                 return info_richtext_;
683         }
684
685         info_ = convertLaTeXCommands(processRichtext(info_, false));
686         return info_;
687 }
688
689
690 docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
691         Buffer const & buf, docstring const & format,
692         CiteItem const & ci, bool next, bool second) const
693 {
694         docstring loclabel;
695
696         int counter = 0;
697         loclabel = expandFormat(format, xrefs, counter, buf, ci, next, second);
698
699         if (!loclabel.empty() && !next) {
700                 loclabel = processRichtext(loclabel, ci.richtext);
701                 loclabel = convertLaTeXCommands(loclabel);
702         }
703
704         return loclabel;
705 }
706
707
708 docstring const & BibTeXInfo::operator[](docstring const & field) const
709 {
710         BibTeXInfo::const_iterator it = find(field);
711         if (it != end())
712                 return it->second;
713         static docstring const empty_value = docstring();
714         return empty_value;
715 }
716
717
718 docstring const & BibTeXInfo::operator[](string const & field) const
719 {
720         return operator[](from_ascii(field));
721 }
722
723
724 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
725         CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
726 {
727         // anything less is pointless
728         LASSERT(maxsize >= 16, maxsize = 16);
729         string key = oldkey;
730         bool cleanit = false;
731         if (prefixIs(oldkey, "clean:")) {
732                 key = oldkey.substr(6);
733                 cleanit = true;
734         }
735
736         docstring ret = operator[](key);
737         if (ret.empty() && !xrefs.empty()) {
738                 vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
739                 vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
740                 for (; it != en; ++it) {
741                         if (*it && !(**it)[key].empty()) {
742                                 ret = (**it)[key];
743                                 break;
744                         }
745                 }
746         }
747         if (ret.empty()) {
748                 // some special keys
749                 // FIXME: dialog, textbefore and textafter have nothing to do with this
750                 if (key == "dialog" && ci.context == CiteItem::Dialog)
751                         ret = from_ascii("x"); // any non-empty string will do
752                 else if (key == "export" && ci.context == CiteItem::Export)
753                         ret = from_ascii("x"); // any non-empty string will do
754                 else if (key == "ifstar" && ci.Starred)
755                         ret = from_ascii("x"); // any non-empty string will do
756                 else if (key == "entrytype")
757                         ret = entry_type_;
758                 else if (prefixIs(key, "ifentrytype:")
759                          && from_ascii(key.substr(12)) == entry_type_)
760                         ret = from_ascii("x"); // any non-empty string will do
761                 else if (key == "key")
762                         ret = bib_key_;
763                 else if (key == "label")
764                         ret = label_;
765                 else if (key == "modifier" && modifier_ != 0)
766                         ret = modifier_;
767                 else if (key == "numericallabel")
768                         ret = cite_number_;
769                 else if (key == "abbrvauthor") {
770                         // Special key to provide abbreviated author names,
771                         // with respect to maxcitenames.
772                         ret = getAuthorList(&buf, false, false);
773                         if (ci.forceUpperCase && isLowerCase(ret[0]))
774                                 ret[0] = uppercase(ret[0]);
775                 } else if (key == "fullauthor") {
776                         // Return a full author list
777                         ret = getAuthorList(&buf, true, false);
778                         if (ci.forceUpperCase && isLowerCase(ret[0]))
779                                 ret[0] = uppercase(ret[0]);
780                 } else if (key == "forceabbrvauthor") {
781                         // Special key to provide abbreviated author names,
782                         // irrespective of maxcitenames.
783                         ret = getAuthorList(&buf, false, true);
784                         if (ci.forceUpperCase && isLowerCase(ret[0]))
785                                 ret[0] = uppercase(ret[0]);
786                 } else if (key == "bibentry") {
787                         // Special key to provide the full bibliography entry: see getInfo()
788                         CiteEngineType const engine_type = buf.params().citeEngineType();
789                         DocumentClass const & dc = buf.params().documentClass();
790                         docstring const & format =
791                                 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_), false));
792                         int counter = 0;
793                         ret = expandFormat(format, xrefs, counter, buf, ci, false, false);
794                 } else if (key == "textbefore")
795                         ret = ci.textBefore;
796                 else if (key == "textafter")
797                         ret = ci.textAfter;
798                 else if (key == "year")
799                         ret = getYear();
800         }
801
802         if (cleanit)
803                 ret = html::cleanAttr(ret);
804
805         // make sure it is not too big
806         support::truncateWithEllipsis(ret, maxsize);
807         return ret;
808 }
809
810
811 //////////////////////////////////////////////////////////////////////
812 //
813 // BiblioInfo
814 //
815 //////////////////////////////////////////////////////////////////////
816
817 namespace {
818
819 // A functor for use with sort, leading to case insensitive sorting
820 class compareNoCase: public binary_function<docstring, docstring, bool>
821 {
822 public:
823         bool operator()(docstring const & s1, docstring const & s2) const {
824                 return compare_no_case(s1, s2) < 0;
825         }
826 };
827
828 } // namespace anon
829
830
831 vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const
832 {
833         vector<docstring> result;
834         if (!data.isBibTeX())
835                 return result;
836         // Legacy crossref field. This is not nestable.
837         if (!nested && !data["crossref"].empty()) {
838                 docstring const xrefkey = data["crossref"];
839                 result.push_back(xrefkey);
840                 // However, check for nested xdatas
841                 BiblioInfo::const_iterator it = find(xrefkey);
842                 if (it != end()) {
843                         BibTeXInfo const & xref = it->second;
844                         vector<docstring> const nxdata = getXRefs(xref, true);
845                         if (!nxdata.empty())
846                                 result.insert(result.end(), nxdata.begin(), nxdata.end());
847                 }
848         }
849         // Biblatex's xdata field. Infinitely nestable.
850         // XData field can consist of a comma-separated list of keys
851         vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
852         if (!xdatakeys.empty()) {
853                 vector<docstring>::const_iterator xit = xdatakeys.begin();
854                 vector<docstring>::const_iterator xen = xdatakeys.end();
855                 for (; xit != xen; ++xit) {
856                         docstring const xdatakey = *xit;
857                         result.push_back(xdatakey);
858                         BiblioInfo::const_iterator it = find(xdatakey);
859                         if (it != end()) {
860                                 BibTeXInfo const & xdata = it->second;
861                                 vector<docstring> const nxdata = getXRefs(xdata, true);
862                                 if (!nxdata.empty())
863                                         result.insert(result.end(), nxdata.begin(), nxdata.end());
864                         }
865                 }
866         }
867         return result;
868 }
869
870
871 vector<docstring> const BiblioInfo::getKeys() const
872 {
873         vector<docstring> bibkeys;
874         BiblioInfo::const_iterator it  = begin();
875         for (; it != end(); ++it)
876                 bibkeys.push_back(it->first);
877         sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
878         return bibkeys;
879 }
880
881
882 vector<docstring> const BiblioInfo::getFields() const
883 {
884         vector<docstring> bibfields;
885         set<docstring>::const_iterator it = field_names_.begin();
886         set<docstring>::const_iterator end = field_names_.end();
887         for (; it != end; ++it)
888                 bibfields.push_back(*it);
889         sort(bibfields.begin(), bibfields.end());
890         return bibfields;
891 }
892
893
894 vector<docstring> const BiblioInfo::getEntries() const
895 {
896         vector<docstring> bibentries;
897         set<docstring>::const_iterator it = entry_types_.begin();
898         set<docstring>::const_iterator end = entry_types_.end();
899         for (; it != end; ++it)
900                 bibentries.push_back(*it);
901         sort(bibentries.begin(), bibentries.end());
902         return bibentries;
903 }
904
905
906 docstring const BiblioInfo::getAuthorList(docstring const & key, Buffer const & buf) 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.getAuthorList(&buf, false);
913 }
914
915
916 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
917 {
918         BiblioInfo::const_iterator it = find(key);
919         if (it == end())
920                 return docstring();
921         BibTeXInfo const & data = it->second;
922         return data.citeNumber();
923 }
924
925
926 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
927 {
928         BiblioInfo::const_iterator it = find(key);
929         if (it == end())
930                 return docstring();
931         BibTeXInfo const & data = it->second;
932         docstring year = data.getYear();
933         if (year.empty()) {
934                 // let's try the crossrefs
935                 vector<docstring> const xrefs = getXRefs(data);
936                 if (xrefs.empty())
937                         // no luck
938                         return docstring();
939                 vector<docstring>::const_iterator it = xrefs.begin();
940                 vector<docstring>::const_iterator en = xrefs.end();
941                 for (; it != en; ++it) {
942                         BiblioInfo::const_iterator const xrefit = find(*it);
943                         if (xrefit == end())
944                                 continue;
945                         BibTeXInfo const & xref_data = xrefit->second;
946                         year = xref_data.getYear();
947                         if (!year.empty())
948                                 // success!
949                                 break;
950                 }
951         }
952         if (use_modifier && data.modifier() != 0)
953                 year += data.modifier();
954         return year;
955 }
956
957
958 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
959 {
960         docstring const year = getYear(key, use_modifier);
961         if (year.empty())
962                 return buf.B_("No year");
963         return year;
964 }
965
966
967 docstring const BiblioInfo::getInfo(docstring const & key,
968         Buffer const & buf, CiteItem const & ci) const
969 {
970         BiblioInfo::const_iterator it = find(key);
971         if (it == end())
972                 return docstring(_("Bibliography entry not found!"));
973         BibTeXInfo const & data = it->second;
974         BibTeXInfoList xrefptrs;
975         vector<docstring> const xrefs = getXRefs(data);
976         if (!xrefs.empty()) {
977                 vector<docstring>::const_iterator it = xrefs.begin();
978                 vector<docstring>::const_iterator en = xrefs.end();
979                 for (; it != en; ++it) {
980                         BiblioInfo::const_iterator const xrefit = find(*it);
981                         if (xrefit != end())
982                                 xrefptrs.push_back(&(xrefit->second));
983                 }
984         }
985         return data.getInfo(xrefptrs, buf, ci);
986 }
987
988
989 docstring const BiblioInfo::getLabel(vector<docstring> keys,
990         Buffer const & buf, string const & style, CiteItem const & ci) const
991 {
992         size_t max_size = ci.max_size;
993         // shorter makes no sense
994         LASSERT(max_size >= 16, max_size = 16);
995
996         // we can't display more than 10 of these, anyway
997         bool const too_many_keys = keys.size() > 10;
998         if (too_many_keys)
999                 keys.resize(10);
1000
1001         CiteEngineType const engine_type = buf.params().citeEngineType();
1002         DocumentClass const & dc = buf.params().documentClass();
1003         docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, false, "cite"));
1004         docstring ret = format;
1005         vector<docstring>::const_iterator key = keys.begin();
1006         vector<docstring>::const_iterator ken = keys.end();
1007         for (int i = 0; key != ken; ++key, ++i) {
1008                 BiblioInfo::const_iterator it = find(*key);
1009                 BibTeXInfo empty_data;
1010                 empty_data.key(*key);
1011                 BibTeXInfo & data = empty_data;
1012                 vector<BibTeXInfo const *> xrefptrs;
1013                 if (it != end()) {
1014                         data = it->second;
1015                         vector<docstring> const xrefs = getXRefs(data);
1016                         if (!xrefs.empty()) {
1017                                 vector<docstring>::const_iterator it = xrefs.begin();
1018                                 vector<docstring>::const_iterator en = xrefs.end();
1019                                 for (; it != en; ++it) {
1020                                         BiblioInfo::const_iterator const xrefit = find(*it);
1021                                         if (xrefit != end())
1022                                                 xrefptrs.push_back(&(xrefit->second));
1023                                 }
1024                         }
1025                 }
1026                 ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
1027         }
1028
1029         if (too_many_keys)
1030                 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
1031         support::truncateWithEllipsis(ret, max_size);
1032         return ret;
1033 }
1034
1035
1036 bool BiblioInfo::isBibtex(docstring const & key) const
1037 {
1038         docstring key1;
1039         split(key, key1, ',');
1040         BiblioInfo::const_iterator it = find(key1);
1041         if (it == end())
1042                 return false;
1043         return it->second.isBibTeX();
1044 }
1045
1046
1047 vector<docstring> const BiblioInfo::getCiteStrings(
1048         vector<docstring> const & keys, vector<CitationStyle> const & styles,
1049         Buffer const & buf, CiteItem const & ci) const
1050 {
1051         if (empty())
1052                 return vector<docstring>();
1053
1054         string style;
1055         vector<docstring> vec(styles.size());
1056         for (size_t i = 0; i != vec.size(); ++i) {
1057                 style = styles[i].name;
1058                 vec[i] = getLabel(keys, buf, style, ci);
1059         }
1060
1061         return vec;
1062 }
1063
1064
1065 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
1066 {
1067         bimap_.insert(info.begin(), info.end());
1068         field_names_.insert(info.field_names_.begin(), info.field_names_.end());
1069         entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
1070 }
1071
1072
1073 namespace {
1074
1075 // used in xhtml to sort a list of BibTeXInfo objects
1076 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
1077 {
1078         docstring const lauth = lhs->getAuthorList();
1079         docstring const rauth = rhs->getAuthorList();
1080         docstring const lyear = lhs->getYear();
1081         docstring const ryear = rhs->getYear();
1082         docstring const ltitl = lhs->operator[]("title");
1083         docstring const rtitl = rhs->operator[]("title");
1084         return  (lauth < rauth)
1085                 || (lauth == rauth && lyear < ryear)
1086                 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
1087 }
1088
1089 }
1090
1091
1092 void BiblioInfo::collectCitedEntries(Buffer const & buf)
1093 {
1094         cited_entries_.clear();
1095         // We are going to collect all the citation keys used in the document,
1096         // getting them from the TOC.
1097         // FIXME We may want to collect these differently, in the first case,
1098         // so that we might have them in order of appearance.
1099         set<docstring> citekeys;
1100         shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1101         Toc::const_iterator it = toc->begin();
1102         Toc::const_iterator const en = toc->end();
1103         for (; it != en; ++it) {
1104                 if (it->str().empty())
1105                         continue;
1106                 vector<docstring> const keys = getVectorFromString(it->str());
1107                 citekeys.insert(keys.begin(), keys.end());
1108         }
1109         if (citekeys.empty())
1110                 return;
1111
1112         // We have a set of the keys used in this document.
1113         // We will now convert it to a list of the BibTeXInfo objects used in
1114         // this document...
1115         vector<BibTeXInfo const *> bi;
1116         set<docstring>::const_iterator cit = citekeys.begin();
1117         set<docstring>::const_iterator const cen = citekeys.end();
1118         for (; cit != cen; ++cit) {
1119                 BiblioInfo::const_iterator const bt = find(*cit);
1120                 if (bt == end() || !bt->second.isBibTeX())
1121                         continue;
1122                 bi.push_back(&(bt->second));
1123         }
1124         // ...and sort it.
1125         sort(bi.begin(), bi.end(), lSorter);
1126
1127         // Now we can write the sorted keys
1128         vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1129         vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1130         for (; bit != ben; ++bit)
1131                 cited_entries_.push_back((*bit)->key());
1132 }
1133
1134
1135 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1136 {
1137         collectCitedEntries(buf);
1138         CiteEngineType const engine_type = buf.params().citeEngineType();
1139         bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1140
1141         int keynumber = 0;
1142         char modifier = 0;
1143         // used to remember the last one we saw
1144         // we'll be comparing entries to see if we need to add
1145         // modifiers, like "1984a"
1146         map<docstring, BibTeXInfo>::iterator last;
1147
1148         vector<docstring>::const_iterator it = cited_entries_.begin();
1149         vector<docstring>::const_iterator const en = cited_entries_.end();
1150         for (; it != en; ++it) {
1151                 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1152                 // this shouldn't happen, but...
1153                 if (biit == bimap_.end())
1154                         // ...fail gracefully, anyway.
1155                         continue;
1156                 BibTeXInfo & entry = biit->second;
1157                 if (numbers) {
1158                         docstring const num = convert<docstring>(++keynumber);
1159                         entry.setCiteNumber(num);
1160                 } else {
1161                         // coverity complains about our derefercing the iterator last,
1162                         // which was not initialized above. but it does get initialized
1163                         // after the first time through the loop, which is the point of
1164                         // the first test.
1165                         // coverity[FORWARD_NULL]
1166                         if (it != cited_entries_.begin()
1167                             && entry.getAuthorList() == last->second.getAuthorList()
1168                             // we access the year via getYear() so as to get it from the xref,
1169                             // if we need to do so
1170                             && getYear(entry.key()) == getYear(last->second.key())) {
1171                                 if (modifier == 0) {
1172                                         // so the last one should have been 'a'
1173                                         last->second.setModifier('a');
1174                                         modifier = 'b';
1175                                 } else if (modifier == 'z')
1176                                         modifier = 'A';
1177                                 else
1178                                         modifier++;
1179                         } else {
1180                                 modifier = 0;
1181                         }
1182                         entry.setModifier(modifier);
1183                         // remember the last one
1184                         last = biit;
1185                 }
1186         }
1187         // Set the labels
1188         it = cited_entries_.begin();
1189         for (; it != en; ++it) {
1190                 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1191                 // this shouldn't happen, but...
1192                 if (biit == bimap_.end())
1193                         // ...fail gracefully, anyway.
1194                         continue;
1195                 BibTeXInfo & entry = biit->second;
1196                 if (numbers) {
1197                         entry.label(entry.citeNumber());
1198                 } else {
1199                         docstring const auth = entry.getAuthorList(&buf, false);
1200                         // we do it this way so as to access the xref, if necessary
1201                         // note that this also gives us the modifier
1202                         docstring const year = getYear(*it, buf, true);
1203                         if (!auth.empty() && !year.empty())
1204                                 entry.label(auth + ' ' + year);
1205                         else
1206                                 entry.label(entry.key());
1207                 }
1208         }
1209 }
1210
1211
1212 //////////////////////////////////////////////////////////////////////
1213 //
1214 // CitationStyle
1215 //
1216 //////////////////////////////////////////////////////////////////////
1217
1218
1219 CitationStyle citationStyleFromString(string const & command,
1220                                       BufferParams const & params)
1221 {
1222         CitationStyle cs;
1223         if (command.empty())
1224                 return cs;
1225
1226         string const alias = params.getCiteAlias(command);
1227         string cmd = alias.empty() ? command : alias;
1228         if (isUpperCase(command[0])) {
1229                 cs.forceUpperCase = true;
1230                 cmd[0] = lowercase(cmd[0]);
1231         }
1232
1233         size_t const n = command.size() - 1;
1234         if (command[n] == '*') {
1235                 cs.hasStarredVersion = true;
1236                 if (suffixIs(cmd, '*'))
1237                         cmd = cmd.substr(0, cmd.size() - 1);
1238         }
1239
1240         cs.name = cmd;
1241         return cs;
1242 }
1243
1244
1245 string citationStyleToString(const CitationStyle & cs, bool const latex)
1246 {
1247         string cmd = latex ? cs.cmd : cs.name;
1248         if (cs.forceUpperCase)
1249                 cmd[0] = uppercase(cmd[0]);
1250         if (cs.hasStarredVersion)
1251                 cmd += '*';
1252         return cmd;
1253 }
1254
1255 } // namespace lyx