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