LCOV - code coverage report
Current view: top level - preproc/refer - label.ypp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 68 622 10.9 %
Date: 2026-01-16 17:51:41 Functions: 13 77 16.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1989-2024 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : %{
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include "refer.h"
      26             : #include "refid.h"
      27             : #include "ref.h"
      28             : #include "token.h"
      29             : 
      30             : int yylex();
      31             : void yyerror(const char *);
      32             : 
      33             : static const char *format_serial(char c, int n);
      34             : 
      35             : struct label_info {
      36             :   int start;
      37             :   int length;
      38             :   int count;
      39             :   int total;
      40             :   label_info(const string &);
      41             : };
      42             : 
      43             : label_info *lookup_label(const string &label);
      44             : 
      45             : struct expression {
      46             :   enum {
      47             :     // Does the tentative label depend on the reference?
      48             :     CONTAINS_VARIABLE = 01,
      49             :     CONTAINS_STAR = 02,
      50             :     CONTAINS_FORMAT = 04,
      51             :     CONTAINS_AT = 010
      52             :   };
      53           0 :   virtual ~expression() { }
      54             :   virtual void evaluate(int, const reference &, string &,
      55             :                         substring_position &) = 0;
      56           0 :   virtual unsigned analyze() { return 0; }
      57             : };
      58             : 
      59             : class at_expr : public expression {
      60             : public:
      61           0 :   at_expr() { }
      62             :   void evaluate(int, const reference &, string &, substring_position &);
      63           0 :   unsigned analyze() { return CONTAINS_VARIABLE|CONTAINS_AT; }
      64             : };
      65             : 
      66             : class format_expr : public expression {
      67             :   char type;
      68             :   int width;
      69             :   int first_number;
      70             : public:
      71           6 :   format_expr(char c, int w = 0, int f = 1)
      72           6 :     : type(c), width(w), first_number(f) { }
      73             :   void evaluate(int, const reference &, string &, substring_position &);
      74           6 :   unsigned analyze() { return CONTAINS_FORMAT; }
      75             : };
      76             : 
      77             : class field_expr : public expression {
      78             :   int number;
      79             :   char name;
      80             : public:
      81           0 :   field_expr(char nm, int num) : number(num), name(nm) { }
      82             :   void evaluate(int, const reference &, string &, substring_position &);
      83           0 :   unsigned analyze() { return CONTAINS_VARIABLE; }
      84             : };
      85             : 
      86             : class literal_expr : public expression {
      87             :   string s;
      88             : public:
      89           0 :   literal_expr(const char *ptr, int len) : s(ptr, len) { }
      90             :   void evaluate(int, const reference &, string &, substring_position &);
      91             : };
      92             : 
      93             : class unary_expr : public expression {
      94             : protected:
      95             :   expression *expr;
      96             : public:
      97           6 :   unary_expr(expression *e) : expr(e) { }
      98           0 :   ~unary_expr() { delete expr; }
      99             :   void evaluate(int, const reference &, string &, substring_position &) = 0;
     100           0 :   unsigned analyze() { return expr ? expr->analyze() : 0; }
     101             : };
     102             : 
     103             : // This caches the analysis of an expression.
     104             : 
     105             : class analyzed_expr : public unary_expr {
     106             :   unsigned flags;
     107             : public:
     108             :   analyzed_expr(expression *);
     109             :   void evaluate(int, const reference &, string &, substring_position &);
     110           5 :   unsigned analyze() { return flags; }
     111             : };
     112             : 
     113             : class star_expr : public unary_expr {
     114             : public:
     115           0 :   star_expr(expression *e) : unary_expr(e) { }
     116             :   void evaluate(int, const reference &, string &, substring_position &);
     117           0 :   unsigned analyze() {
     118           0 :     return ((expr ? (expr->analyze() & ~CONTAINS_VARIABLE) : 0)
     119           0 :             | CONTAINS_STAR);
     120             :   }
     121             : };
     122             : 
     123             : typedef void map_func(const char *, const char *, string &);
     124             : 
     125             : class map_expr : public unary_expr {
     126             :   map_func *func;
     127             : public:
     128           0 :   map_expr(expression *e, map_func *f) : unary_expr(e), func(f) { }
     129             :   void evaluate(int, const reference &, string &, substring_position &);
     130             : };
     131             : 
     132             : typedef const char *extractor_func(const char *, const char *, const char **);
     133             : 
     134             : class extractor_expr : public unary_expr {
     135             :   int part;
     136             :   extractor_func *func;
     137             : public:
     138             :   enum { BEFORE = +1, MATCH = 0, AFTER = -1 };
     139           0 :   extractor_expr(expression *e, extractor_func *f, int pt)
     140           0 :     : unary_expr(e), part(pt), func(f) { }
     141             :   void evaluate(int, const reference &, string &, substring_position &);
     142             : };
     143             : 
     144             : class truncate_expr : public unary_expr {
     145             :   int n;
     146             : public:
     147           0 :   truncate_expr(expression *e, int i) : unary_expr(e), n(i) { }
     148             :   void evaluate(int, const reference &, string &, substring_position &);
     149             : };
     150             : 
     151             : class separator_expr : public unary_expr {
     152             : public:
     153           0 :   separator_expr(expression *e) : unary_expr(e) { }
     154             :   void evaluate(int, const reference &, string &, substring_position &);
     155             : };
     156             : 
     157             : class binary_expr : public expression {
     158             : protected:
     159             :   expression *expr1;
     160             :   expression *expr2;
     161             : public:
     162           0 :   binary_expr(expression *e1, expression *e2) : expr1(e1), expr2(e2) { }
     163           0 :   ~binary_expr() { delete expr1; delete expr2; }
     164             :   void evaluate(int, const reference &, string &, substring_position &) = 0;
     165           0 :   unsigned analyze() {
     166           0 :     return (expr1 ? expr1->analyze() : 0) | (expr2 ? expr2->analyze() : 0);
     167             :   }
     168             : };
     169             : 
     170             : class alternative_expr : public binary_expr {
     171             : public:
     172           0 :   alternative_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
     173             :   void evaluate(int, const reference &, string &, substring_position &);
     174             : };
     175             : 
     176             : class list_expr : public binary_expr {
     177             : public:
     178           0 :   list_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
     179             :   void evaluate(int, const reference &, string &, substring_position &);
     180             : };
     181             : 
     182             : class substitute_expr : public binary_expr {
     183             : public:
     184           0 :   substitute_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
     185             :   void evaluate(int, const reference &, string &, substring_position &);
     186             : };
     187             : 
     188             : class ternary_expr : public expression {
     189             : protected:
     190             :   expression *expr1;
     191             :   expression *expr2;
     192             :   expression *expr3;
     193             : public:
     194           0 :   ternary_expr(expression *e1, expression *e2, expression *e3)
     195           0 :     : expr1(e1), expr2(e2), expr3(e3) { }
     196           0 :   ~ternary_expr() { delete expr1; delete expr2; delete expr3; }
     197             :   void evaluate(int, const reference &, string &, substring_position &) = 0;
     198           0 :   unsigned analyze() {
     199           0 :     return ((expr1 ? expr1->analyze() : 0)
     200           0 :             | (expr2 ? expr2->analyze() : 0)
     201           0 :             | (expr3 ? expr3->analyze() : 0));
     202             :   }
     203             : };
     204             : 
     205             : class conditional_expr : public ternary_expr {
     206             : public:
     207           0 :   conditional_expr(expression *e1, expression *e2, expression *e3)
     208           0 :     : ternary_expr(e1, e2, e3) { }
     209             :   void evaluate(int, const reference &, string &, substring_position &);
     210             : };
     211             : 
     212             : static expression *parsed_label = 0 /* nullptr */;
     213             : static expression *parsed_date_label = 0 /* nullptr */;
     214             : static expression *parsed_short_label = 0 /* nullptr */;
     215             : 
     216             : static expression *parse_result;
     217             : 
     218             : string literals;
     219             : 
     220             : %}
     221             : 
     222             : %union {
     223             :   int num;
     224             :   expression *expr;
     225             :   struct dig_s { int ndigits; int val; } dig;
     226             :   struct str_s { int start; int len; } str;
     227             : }
     228             : 
     229             : /* uppercase or lowercase letter */
     230             : %token <num> TOKEN_LETTER
     231             : /* literal characters */
     232             : %token <str> TOKEN_LITERAL
     233             : /* digit */
     234             : %token <num> TOKEN_DIGIT
     235             : 
     236             : %type <expr> conditional
     237             : %type <expr> alternative
     238             : %type <expr> list
     239             : %type <expr> string
     240             : %type <expr> substitute
     241             : %type <expr> optional_conditional
     242             : %type <num> number
     243             : %type <dig> digits
     244             : %type <num> optional_number
     245             : %type <num> flag
     246             : 
     247             : %%
     248             : 
     249             : expr:
     250             :         optional_conditional
     251           6 :                 { parse_result = ($1 ? new analyzed_expr($1) : 0); }
     252             :         ;
     253             : 
     254             : conditional:
     255             :         alternative
     256           6 :                 { $$ = $1; }
     257             :         | alternative '?' optional_conditional ':' conditional
     258           0 :                 { $$ = new conditional_expr($1, $3, $5); }
     259             :         ;
     260             : 
     261             : optional_conditional:
     262             :         /* empty */
     263           0 :                 { $$ = 0; }
     264             :         | conditional
     265           6 :                 { $$ = $1; }
     266             :         ;
     267             : 
     268             : alternative:
     269             :         list
     270           6 :                 { $$ = $1; }
     271             :         | alternative '|' list
     272           0 :                 { $$ = new alternative_expr($1, $3); }
     273             :         | alternative '&' list
     274           0 :                 { $$ = new conditional_expr($1, $3, 0); }
     275             :         ;
     276             : 
     277             : list:
     278             :         substitute
     279           6 :                 { $$ = $1; }
     280             :         | list substitute
     281           0 :                 { $$ = new list_expr($1, $2); }
     282             :         ;
     283             : 
     284             : substitute:
     285             :         string
     286           6 :                 { $$ = $1; }
     287             :         | substitute '~' string
     288           0 :                 { $$ = new substitute_expr($1, $3); }
     289             :         ;
     290             : 
     291             : string:
     292             :         '@'
     293           0 :                 { $$ = new at_expr; }
     294             :         | TOKEN_LITERAL
     295             :                 {
     296           0 :                   $$ = new literal_expr(literals.contents() + $1.start,
     297           0 :                                         $1.len);
     298             :                 }
     299             :         | TOKEN_LETTER
     300           0 :                 { $$ = new field_expr($1, 0); }
     301             :         | TOKEN_LETTER number
     302           0 :                 { $$ = new field_expr($1, $2 - 1); }
     303             :         | '%' TOKEN_LETTER
     304             :                 {
     305           0 :                   switch ($2) {
     306           0 :                   case 'I':
     307             :                   case 'i':
     308             :                   case 'A':
     309             :                   case 'a':
     310           0 :                     $$ = new format_expr($2);
     311           0 :                     break;
     312           0 :                   default:
     313           0 :                     command_error("unrecognized format '%1'", char($2));
     314           0 :                     $$ = new format_expr('a');
     315           0 :                     break;
     316             :                   }
     317             :                 }
     318             : 
     319             :         | '%' digits
     320             :                 {
     321           6 :                   $$ = new format_expr('0', $2.ndigits, $2.val);
     322             :                 }
     323             :         | string '.' flag TOKEN_LETTER optional_number
     324             :                 {
     325           0 :                   switch ($4) {
     326           0 :                   case 'l':
     327           0 :                     $$ = new map_expr($1, lowercase);
     328           0 :                     break;
     329           0 :                   case 'u':
     330           0 :                     $$ = new map_expr($1, uppercase);
     331           0 :                     break;
     332           0 :                   case 'c':
     333           0 :                     $$ = new map_expr($1, capitalize);
     334           0 :                     break;
     335           0 :                   case 'r':
     336           0 :                     $$ = new map_expr($1, reverse_name);
     337           0 :                     break;
     338           0 :                   case 'a':
     339           0 :                     $$ = new map_expr($1, abbreviate_name);
     340           0 :                     break;
     341           0 :                   case 'y':
     342           0 :                     $$ = new extractor_expr($1, find_year, $3);
     343           0 :                     break;
     344           0 :                   case 'n':
     345           0 :                     $$ = new extractor_expr($1, find_last_name, $3);
     346           0 :                     break;
     347           0 :                   default:
     348           0 :                     $$ = $1;
     349           0 :                     command_error("unknown function '%1'", char($4));
     350           0 :                     break;
     351             :                   }
     352             :                 }
     353             : 
     354             :         | string '+' number
     355           0 :                 { $$ = new truncate_expr($1, $3); }
     356             :         | string '-' number
     357           0 :                 { $$ = new truncate_expr($1, -$3); }
     358             :         | string '*'
     359           0 :                 { $$ = new star_expr($1); }
     360             :         | '(' optional_conditional ')'
     361           0 :                 { $$ = $2; }
     362             :         | '<' optional_conditional '>'
     363           0 :                 { $$ = new separator_expr($2); }
     364             :         ;
     365             : 
     366             : optional_number:
     367             :         /* empty */
     368           0 :                 { $$ = -1; }
     369             :         | number
     370           0 :                 { $$ = $1; }
     371             :         ;
     372             : 
     373             : number:
     374             :         TOKEN_DIGIT
     375           0 :                 { $$ = $1; }
     376             :         | number TOKEN_DIGIT
     377           0 :                 { $$ = $1*10 + $2; }
     378             :         ;
     379             : 
     380             : digits:
     381             :         TOKEN_DIGIT
     382           6 :                 { $$.ndigits = 1; $$.val = $1; }
     383             :         | digits TOKEN_DIGIT
     384           0 :                 { $$.ndigits = $1.ndigits + 1; $$.val = $1.val*10 + $2; }
     385             :         ;
     386             : 
     387             : 
     388             : flag:
     389             :         /* empty */
     390           0 :                 { $$ = 0; }
     391             :         | '+'
     392           0 :                 { $$ = 1; }
     393             :         | '-'
     394           0 :                 { $$ = -1; }
     395             :         ;
     396             : 
     397             : %%
     398             : 
     399             : /* bison defines const to be empty unless __STDC__ is defined, which it
     400             : isn't under cfront */
     401             : 
     402             : #ifdef const
     403             : #undef const
     404             : #endif
     405             : 
     406             : const char *spec_ptr;
     407             : const char *spec_end;
     408             : const char *spec_cur;
     409             : 
     410             : static char uppercase_array[] = {
     411             :   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
     412             :   'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
     413             :   'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
     414             :   'Y', 'Z',
     415             : };
     416             : 
     417             : static char lowercase_array[] = {
     418             :   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     419             :   'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
     420             :   'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
     421             :   'y', 'z',
     422             : };
     423             : 
     424          18 : int yylex()
     425             : {
     426          18 :   while (spec_ptr < spec_end && csspace(*spec_ptr))
     427           0 :     spec_ptr++;
     428          18 :   spec_cur = spec_ptr;
     429          18 :   if (spec_ptr >= spec_end)
     430           6 :     return 0;
     431          12 :   unsigned char c = *spec_ptr++;
     432          12 :   if (csalpha(c)) {
     433           0 :     yylval.num = c;
     434           0 :     return TOKEN_LETTER;
     435             :   }
     436          12 :   if (csdigit(c)) {
     437           6 :     yylval.num = c - '0';
     438           6 :     return TOKEN_DIGIT;
     439             :   }
     440           6 :   if (c == '\'') {
     441           0 :     yylval.str.start = literals.length();
     442           0 :     for (; spec_ptr < spec_end; spec_ptr++) {
     443           0 :       if (*spec_ptr == '\'') {
     444           0 :         if (++spec_ptr < spec_end && *spec_ptr == '\'')
     445           0 :           literals += '\'';
     446             :         else {
     447           0 :           yylval.str.len = literals.length() - yylval.str.start;
     448           0 :           return TOKEN_LITERAL;
     449             :         }
     450             :       }
     451             :       else
     452           0 :         literals += *spec_ptr;
     453             :     }
     454           0 :     yylval.str.len = literals.length() - yylval.str.start;
     455           0 :     return TOKEN_LITERAL;
     456             :   }
     457           6 :   return c;
     458             : }
     459             : 
     460           6 : int set_label_spec(const char *label_spec)
     461             : {
     462           6 :   spec_cur = spec_ptr = label_spec;
     463           6 :   spec_end = strchr(label_spec, '\0');
     464           6 :   literals.clear();
     465           6 :   if (yyparse())
     466           0 :     return 0;
     467           6 :   delete parsed_label;
     468           6 :   parsed_label = parse_result;
     469           6 :   return 1;
     470             : }
     471             : 
     472           0 : int set_date_label_spec(const char *label_spec)
     473             : {
     474           0 :   spec_cur = spec_ptr = label_spec;
     475           0 :   spec_end = strchr(label_spec, '\0');
     476           0 :   literals.clear();
     477           0 :   if (yyparse())
     478           0 :     return 0;
     479           0 :   delete parsed_date_label;
     480           0 :   parsed_date_label = parse_result;
     481           0 :   return 1;
     482             : }
     483             : 
     484           0 : int set_short_label_spec(const char *label_spec)
     485             : {
     486           0 :   spec_cur = spec_ptr = label_spec;
     487           0 :   spec_end = strchr(label_spec, '\0');
     488           0 :   literals.clear();
     489           0 :   if (yyparse())
     490           0 :     return 0;
     491           0 :   delete parsed_short_label;
     492           0 :   parsed_short_label = parse_result;
     493           0 :   return 1;
     494             : }
     495             : 
     496           0 : void yyerror(const char *message)
     497             : {
     498           0 :   if (spec_cur < spec_end)
     499           0 :     command_error("label specification %1 before '%2'", message, spec_cur);
     500             :   else
     501           0 :     command_error("label specification %1 at end of string",
     502           0 :                   message, spec_cur);
     503           0 : }
     504             : 
     505           0 : void at_expr::evaluate(int tentative, const reference &ref,
     506             :                        string &result, substring_position &)
     507             : {
     508           0 :   if (tentative)
     509           0 :     ref.canonicalize_authors(result);
     510             :   else {
     511           0 :     const char *end, *start = ref.get_authors(&end);
     512           0 :     if (start)
     513           0 :       result.append(start, end - start);
     514             :   }
     515           0 : }
     516             : 
     517           5 : void format_expr::evaluate(int tentative, const reference &ref,
     518             :                            string &result, substring_position &)
     519             : {
     520           5 :   if (tentative)
     521           0 :     return;
     522           5 :   const label_info *lp = ref.get_label_ptr();
     523           5 :   int num = lp == 0 ? ref.get_number() : lp->count;
     524           5 :   if (type != '0')
     525           0 :     result += format_serial(type, num + 1);
     526             :   else {
     527           5 :     const char *ptr = i_to_a(num + first_number);
     528           5 :     ptrdiff_t pad = width - strlen(ptr);
     529           5 :     while (--pad >= 0)
     530           0 :       result += '0';
     531           5 :     result += ptr;
     532             :   }
     533             : }
     534             : 
     535           0 : static const char *format_serial(char c, int n)
     536             : {
     537           0 :   assert(n > 0);
     538             :   static char buf[128]; // more than enough.
     539           0 :   switch (c) {
     540           0 :   case 'i':
     541             :   case 'I':
     542             :     {
     543           0 :       char *p = buf;
     544             :       // troff uses z and w to represent 10000 and 5000 in Roman
     545             :       // numerals; I can find no historical basis for this usage
     546           0 :       const char *s = c == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
     547           0 :       if (n >= 40000)
     548           0 :         return i_to_a(n);
     549           0 :       while (n >= 10000) {
     550           0 :         *p++ = s[0];
     551           0 :         n -= 10000;
     552             :       }
     553           0 :       for (int i = 1000; i > 0; i /= 10, s += 2) {
     554           0 :         int m = n/i;
     555           0 :         n -= m*i;
     556             :         switch (m) {
     557           0 :         case 3:
     558           0 :           *p++ = s[2];
     559             :           /* falls through */
     560           0 :         case 2:
     561           0 :           *p++ = s[2];
     562             :           /* falls through */
     563           0 :         case 1:
     564           0 :           *p++ = s[2];
     565           0 :           break;
     566           0 :         case 4:
     567           0 :           *p++ = s[2];
     568           0 :           *p++ = s[1];
     569           0 :           break;
     570           0 :         case 8:
     571           0 :           *p++ = s[1];
     572           0 :           *p++ = s[2];
     573           0 :           *p++ = s[2];
     574           0 :           *p++ = s[2];
     575           0 :           break;
     576           0 :         case 7:
     577           0 :           *p++ = s[1];
     578           0 :           *p++ = s[2];
     579           0 :           *p++ = s[2];
     580           0 :           break;
     581           0 :         case 6:
     582           0 :           *p++ = s[1];
     583           0 :           *p++ = s[2];
     584           0 :           break;
     585           0 :         case 5:
     586           0 :           *p++ = s[1];
     587           0 :           break;
     588           0 :         case 9:
     589           0 :           *p++ = s[2];
     590           0 :           *p++ = s[0];
     591             :         }
     592             :       }
     593           0 :       *p = 0;
     594           0 :       break;
     595             :     }
     596           0 :   case 'a':
     597             :   case 'A':
     598             :     {
     599           0 :       char *p = buf;
     600             :       // this is derived from troff/reg.c
     601           0 :       while (n > 0) {
     602           0 :         int d = n % 26;
     603           0 :         if (d == 0)
     604           0 :           d = 26;
     605           0 :         n -= d;
     606           0 :         n /= 26;
     607           0 :         *p++ = c == 'a' ? lowercase_array[d - 1] :
     608           0 :                                uppercase_array[d - 1];
     609             :       }
     610           0 :       *p-- = 0;
     611             :       // Reverse it.
     612           0 :       char *q = buf;
     613           0 :       while (q < p) {
     614           0 :         char temp = *q;
     615           0 :         *q = *p;
     616           0 :         *p = temp;
     617           0 :         --p;
     618           0 :         ++q;
     619             :       }
     620           0 :       break;
     621             :     }
     622           0 :   default:
     623           0 :     assert(0 == "unhandled case of register format");
     624             :   }
     625           0 :   return buf;
     626             : }
     627             : 
     628           0 : void field_expr::evaluate(int, const reference &ref,
     629             :                           string &result, substring_position &)
     630             : {
     631             :   const char *end;
     632           0 :   const char *start = ref.get_field(name, &end);
     633           0 :   if (start) {
     634           0 :     start = nth_field(number, start, &end);
     635           0 :     if (start)
     636           0 :       result.append(start, end - start);
     637             :   }
     638           0 : }
     639             : 
     640           0 : void literal_expr::evaluate(int, const reference &,
     641             :                             string &result, substring_position &)
     642             : {
     643           0 :   result += s;
     644           0 : }
     645             : 
     646           6 : analyzed_expr::analyzed_expr(expression *e)
     647           6 : : unary_expr(e), flags(e ? e->analyze() : 0)
     648             : {
     649           6 : }
     650             : 
     651           5 : void analyzed_expr::evaluate(int tentative, const reference &ref,
     652             :                              string &result, substring_position &pos)
     653             : {
     654           5 :   if (expr)
     655           5 :     expr->evaluate(tentative, ref, result, pos);
     656           5 : }
     657             : 
     658           0 : void star_expr::evaluate(int tentative, const reference &ref,
     659             :                          string &result, substring_position &pos)
     660             : {
     661           0 :   const label_info *lp = ref.get_label_ptr();
     662           0 :   if (!tentative
     663           0 :       && (lp == 0 || lp->total > 1)
     664           0 :       && expr)
     665           0 :     expr->evaluate(tentative, ref, result, pos);
     666           0 : }
     667             : 
     668           0 : void separator_expr::evaluate(int tentative, const reference &ref,
     669             :                               string &result, substring_position &pos)
     670             : {
     671           0 :   int start_length = result.length();
     672           0 :   int is_first = pos.start < 0;
     673           0 :   if (expr)
     674           0 :     expr->evaluate(tentative, ref, result, pos);
     675           0 :   if (is_first) {
     676           0 :     pos.start = start_length;
     677           0 :     pos.length = result.length() - start_length;
     678             :   }
     679           0 : }
     680             : 
     681           0 : void map_expr::evaluate(int tentative, const reference &ref,
     682             :                         string &result, substring_position &)
     683             : {
     684           0 :   if (expr) {
     685           0 :     string temp;
     686           0 :     substring_position temp_pos;
     687           0 :     expr->evaluate(tentative, ref, temp, temp_pos);
     688           0 :     (*func)(temp.contents(), temp.contents() + temp.length(), result);
     689             :   }
     690           0 : }
     691             : 
     692           0 : void extractor_expr::evaluate(int tentative, const reference &ref,
     693             :                               string &result, substring_position &)
     694             : {
     695           0 :   if (expr) {
     696           0 :     string temp;
     697           0 :     substring_position temp_pos;
     698           0 :     expr->evaluate(tentative, ref, temp, temp_pos);
     699           0 :     const char *end, *start = (*func)(temp.contents(),
     700           0 :                                       temp.contents() + temp.length(),
     701             :                                       &end);
     702           0 :     switch (part) {
     703           0 :     case BEFORE:
     704           0 :       if (start)
     705           0 :         result.append(temp.contents(), start - temp.contents());
     706             :       else
     707           0 :         result += temp;
     708           0 :       break;
     709           0 :     case MATCH:
     710           0 :       if (start)
     711           0 :         result.append(start, end - start);
     712           0 :       break;
     713           0 :     case AFTER:
     714           0 :       if (start)
     715           0 :         result.append(end, temp.contents() + temp.length() - end);
     716           0 :       break;
     717           0 :     default:
     718           0 :       assert(0 == "unhandled case of part in expression evaluator");
     719             :     }
     720             :   }
     721           0 : }
     722             : 
     723           0 : static void first_part(int len, const char *ptr, const char *end,
     724             :                           string &result)
     725             : {
     726             :   for (;;) {
     727           0 :     const char *token_start = ptr;
     728           0 :     if (!get_token(&ptr, end))
     729           0 :       break;
     730           0 :     const token_info *ti = lookup_token(token_start, ptr);
     731           0 :     int counts = ti->sortify_non_empty(token_start, ptr);
     732           0 :     if (counts && --len < 0)
     733           0 :       break;
     734           0 :     if (counts || ti->is_accent())
     735           0 :       result.append(token_start, ptr - token_start);
     736           0 :   }
     737           0 : }
     738             : 
     739           0 : static void last_part(int len, const char *ptr, const char *end,
     740             :                       string &result)
     741             : {
     742           0 :   const char *start = ptr;
     743           0 :   int count = 0;
     744             :   for (;;) {
     745           0 :     const char *token_start = ptr;
     746           0 :     if (!get_token(&ptr, end))
     747           0 :       break;
     748           0 :     const token_info *ti = lookup_token(token_start, ptr);
     749           0 :     if (ti->sortify_non_empty(token_start, ptr))
     750           0 :       count++;
     751           0 :   }
     752           0 :   ptr = start;
     753           0 :   int skip = count - len;
     754           0 :   if (skip > 0) {
     755             :     for (;;) {
     756           0 :       const char *token_start = ptr;
     757           0 :       if (!get_token(&ptr, end))
     758           0 :         assert(0);
     759           0 :       const token_info *ti = lookup_token(token_start, ptr);
     760           0 :       if (ti->sortify_non_empty(token_start, ptr) && --skip < 0) {
     761           0 :         ptr = token_start;
     762           0 :         break;
     763             :       }
     764           0 :     }
     765             :   }
     766           0 :   first_part(len, ptr, end, result);
     767           0 : }
     768             : 
     769           0 : void truncate_expr::evaluate(int tentative, const reference &ref,
     770             :                              string &result, substring_position &)
     771             : {
     772           0 :   if (expr) {
     773           0 :     string temp;
     774           0 :     substring_position temp_pos;
     775           0 :     expr->evaluate(tentative, ref, temp, temp_pos);
     776           0 :     const char *start = temp.contents();
     777           0 :     const char *end = start + temp.length();
     778           0 :     if (n > 0)
     779           0 :       first_part(n, start, end, result);
     780           0 :     else if (n < 0)
     781           0 :       last_part(-n, start, end, result);
     782             :   }
     783           0 : }
     784             : 
     785           0 : void alternative_expr::evaluate(int tentative, const reference &ref,
     786             :                                 string &result, substring_position &pos)
     787             : {
     788           0 :   int start_length = result.length();
     789           0 :   if (expr1)
     790           0 :     expr1->evaluate(tentative, ref, result, pos);
     791           0 :   if (result.length() == start_length && expr2)
     792           0 :     expr2->evaluate(tentative, ref, result, pos);
     793           0 : }
     794             : 
     795           0 : void list_expr::evaluate(int tentative, const reference &ref,
     796             :                          string &result, substring_position &pos)
     797             : {
     798           0 :   if (expr1)
     799           0 :     expr1->evaluate(tentative, ref, result, pos);
     800           0 :   if (expr2)
     801           0 :     expr2->evaluate(tentative, ref, result, pos);
     802           0 : }
     803             : 
     804           0 : void substitute_expr::evaluate(int tentative, const reference &ref,
     805             :                                string &result, substring_position &pos)
     806             : {
     807           0 :   int start_length = result.length();
     808           0 :   if (expr1)
     809           0 :     expr1->evaluate(tentative, ref, result, pos);
     810           0 :   if (result.length() > start_length && result[result.length() - 1] == '-') {
     811             :     // ought to see if pos covers the -
     812           0 :     result.set_length(result.length() - 1);
     813           0 :     if (expr2)
     814           0 :       expr2->evaluate(tentative, ref, result, pos);
     815             :   }
     816           0 : }
     817             : 
     818           0 : void conditional_expr::evaluate(int tentative, const reference &ref,
     819             :                                 string &result, substring_position &pos)
     820             : {
     821           0 :   string temp;
     822           0 :   substring_position temp_pos;
     823           0 :   if (expr1)
     824           0 :     expr1->evaluate(tentative, ref, temp, temp_pos);
     825           0 :   if (temp.length() > 0) {
     826           0 :     if (expr2)
     827           0 :       expr2->evaluate(tentative, ref, result, pos);
     828             :   }
     829             :   else {
     830           0 :     if (expr3)
     831           0 :       expr3->evaluate(tentative, ref, result, pos);
     832             :   }
     833           0 : }
     834             : 
     835           5 : void reference::pre_compute_label()
     836             : {
     837          10 :   if (parsed_label != 0
     838           5 :       && (parsed_label->analyze() & expression::CONTAINS_VARIABLE)) {
     839           0 :     label.clear();
     840           0 :     substring_position temp_pos;
     841           0 :     parsed_label->evaluate(1, *this, label, temp_pos);
     842           0 :     label_ptr = lookup_label(label);
     843             :   }
     844           5 : }
     845             : 
     846           5 : void reference::compute_label()
     847             : {
     848           5 :   label.clear();
     849           5 :   if (parsed_label)
     850           5 :     parsed_label->evaluate(0, *this, label, separator_pos);
     851           5 :   if (short_label_flag && parsed_short_label)
     852           0 :     parsed_short_label->evaluate(0, *this, short_label, short_separator_pos);
     853           5 :   if (date_as_label) {
     854           0 :     string new_date;
     855           0 :     if (parsed_date_label) {
     856           0 :       substring_position temp_pos;
     857           0 :       parsed_date_label->evaluate(0, *this, new_date, temp_pos);
     858             :     }
     859           0 :     set_date(new_date);
     860             :   }
     861           5 :   if (label_ptr)
     862           0 :     label_ptr->count += 1;
     863           5 : }
     864             : 
     865           5 : void reference::immediate_compute_label()
     866             : {
     867           5 :   if (label_ptr)
     868           0 :     label_ptr->total = 2;    // force use of disambiguator
     869           5 :   compute_label();
     870           5 : }
     871             : 
     872           0 : int reference::merge_labels(reference **v, int n, label_type type,
     873             :                             string &result)
     874             : {
     875           0 :   if (abbreviate_label_ranges)
     876           0 :     return merge_labels_by_number(v, n, type, result);
     877             :   else
     878           0 :     return merge_labels_by_parts(v, n, type, result);
     879             : }
     880             : 
     881           0 : int reference::merge_labels_by_number(reference **v, int n, label_type type,
     882             :                                       string &result)
     883             : {
     884           0 :   if (n <= 1)
     885           0 :     return 0;
     886           0 :   int num = get_number();
     887             :   // Only merge three or more labels.
     888           0 :   if (v[0]->get_number() != num + 1
     889           0 :       || v[1]->get_number() != num + 2)
     890           0 :     return 0;
     891             :   int i;
     892           0 :   for (i = 2; i < n; i++)
     893           0 :     if (v[i]->get_number() != num + i + 1)
     894           0 :       break;
     895           0 :   result = get_label(type);
     896           0 :   result += label_range_indicator;
     897           0 :   result += v[i - 1]->get_label(type);
     898           0 :   return i;
     899             : }
     900             : 
     901           0 : const substring_position &reference::get_separator_pos(label_type type) const
     902             : {
     903           0 :   if (type == SHORT_LABEL && short_label_flag)
     904           0 :     return short_separator_pos;
     905             :   else
     906           0 :     return separator_pos;
     907             : }
     908             : 
     909           5 : const string &reference::get_label(label_type type) const
     910             : {
     911           5 :   if (type == SHORT_LABEL && short_label_flag)
     912           0 :     return short_label;
     913             :   else
     914           5 :     return label;
     915             : }
     916             : 
     917           0 : int reference::merge_labels_by_parts(reference **v, int n, label_type type,
     918             :                                      string &result)
     919             : {
     920           0 :   if (n <= 0)
     921           0 :     return 0;
     922           0 :   const string &lb = get_label(type);
     923           0 :   const substring_position &sp = get_separator_pos(type);
     924           0 :   if (sp.start < 0
     925           0 :       || sp.start != v[0]->get_separator_pos(type).start
     926           0 :       || memcmp(lb.contents(), v[0]->get_label(type).contents(),
     927           0 :                 sp.start) != 0)
     928           0 :     return 0;
     929           0 :   result = lb;
     930           0 :   int i = 0;
     931           0 :   do {
     932           0 :     result += separate_label_second_parts;
     933           0 :     const substring_position &s = v[i]->get_separator_pos(type);
     934           0 :     int sep_end_pos = s.start + s.length;
     935           0 :     result.append(v[i]->get_label(type).contents() + sep_end_pos,
     936           0 :                   v[i]->get_label(type).length() - sep_end_pos);
     937             :   } while (++i < n
     938           0 :            && sp.start == v[i]->get_separator_pos(type).start
     939           0 :            && memcmp(lb.contents(), v[i]->get_label(type).contents(),
     940           0 :                      sp.start) == 0);
     941           0 :   return i;
     942             : }
     943             : 
     944             : string label_pool;
     945             : 
     946           0 : label_info::label_info(const string &s)
     947           0 : : start(label_pool.length()), length(s.length()), count(0), total(1)
     948             : {
     949           0 :   label_pool += s;
     950           0 : }
     951             : 
     952             : static label_info **label_table = 0;
     953             : static int label_table_size = 0;
     954             : static int label_table_used = 0;
     955             : 
     956           0 : label_info *lookup_label(const string &label)
     957             : {
     958           0 :   if (label_table == 0) {
     959           0 :     label_table = new label_info *[17];
     960           0 :     label_table_size = 17;
     961           0 :     for (int i = 0; i < 17; i++)
     962           0 :       label_table[i] = 0;
     963             :   }
     964           0 :   unsigned h = hash_string(label.contents(), label.length()) % label_table_size;
     965             :   label_info **ptr;
     966           0 :   for (ptr = label_table + h;
     967           0 :        *ptr != 0;
     968           0 :        (ptr == label_table)
     969           0 :        ? (ptr = label_table + label_table_size - 1)
     970             :        : ptr--)
     971           0 :     if ((*ptr)->length == label.length()
     972           0 :         && memcmp(label_pool.contents() + (*ptr)->start, label.contents(),
     973           0 :                   label.length()) == 0) {
     974           0 :       (*ptr)->total += 1;
     975           0 :       return *ptr;
     976             :     }
     977           0 :   label_info *result = *ptr = new label_info(label);
     978           0 :   if (++label_table_used * 2 > label_table_size) {
     979             :     // Rehash the table.
     980           0 :     label_info **old_table = label_table;
     981           0 :     int old_size = label_table_size;
     982           0 :     label_table_size = next_size(label_table_size);
     983           0 :     label_table = new label_info *[label_table_size];
     984             :     int i;
     985           0 :     for (i = 0; i < label_table_size; i++)
     986           0 :       label_table[i] = 0;
     987           0 :     for (i = 0; i < old_size; i++)
     988           0 :       if (old_table[i]) {
     989           0 :         h = hash_string(label_pool.contents() + old_table[i]->start,
     990           0 :                         old_table[i]->length);
     991             :         label_info **p;
     992           0 :         for (p = label_table + (h % label_table_size);
     993           0 :              *p != 0;
     994           0 :              (p == label_table)
     995           0 :              ? (p = label_table + label_table_size - 1)
     996             :              : --p)
     997             :             ;
     998           0 :         *p = old_table[i];
     999             :         }
    1000           0 :     delete[] old_table;
    1001             :   }
    1002           0 :   return result;
    1003             : }
    1004             : 
    1005           0 : void clear_labels()
    1006             : {
    1007           0 :   for (int i = 0; i < label_table_size; i++) {
    1008           0 :     delete label_table[i];
    1009           0 :     label_table[i] = 0;
    1010             :   }
    1011           0 :   label_table_used = 0;
    1012           0 :   label_pool.clear();
    1013           0 : }
    1014             : 
    1015             : static void consider_authors(reference **start, reference **end, int i);
    1016             : 
    1017           0 : void compute_labels(reference **v, int n)
    1018             : {
    1019           0 :   if (parsed_label
    1020           0 :       && (parsed_label->analyze() & expression::CONTAINS_AT)
    1021           0 :       && sort_fields.length() >= 2
    1022           0 :       && sort_fields[0] == 'A'
    1023           0 :       && sort_fields[1] == '+')
    1024           0 :     consider_authors(v, v + n, 0);
    1025           0 :   for (int i = 0; i < n; i++)
    1026           0 :     v[i]->compute_label();
    1027           0 : }
    1028             : 
    1029             : 
    1030             : /* A reference with a list of authors <A0,A1,...,AN> _needs_ author i
    1031             : where 0 <= i <= N if there exists a reference with a list of authors
    1032             : <B0,B1,...,BM> such that <A0,A1,...,AN> != <B0,B1,...,BM> and M >= i
    1033             : and Aj = Bj for 0 <= j < i. In this case if we can't say "A0,
    1034             : A1,...,A(i-1) et al" because this would match both <A0,A1,...,AN> and
    1035             : <B0,B1,...,BM>.  If a reference needs author i we only have to call
    1036             : need_author(j) for some j >= i such that the reference also needs
    1037             : author j. */
    1038             : 
    1039             : /* This function handles 2 tasks:
    1040             : determine which authors are needed (cannot be elided with et al.);
    1041             : determine which authors can have only last names in the labels.
    1042             : 
    1043             : References >= start and < end have the same first i author names.
    1044             : Also they're sorted by A+. */
    1045             : 
    1046           0 : static void consider_authors(reference **start, reference **end, int i)
    1047             : {
    1048           0 :   if (start >= end)
    1049           0 :     return;
    1050           0 :   reference **p = start;
    1051           0 :   if (i >= (*p)->get_nauthors()) {
    1052           0 :     for (++p; p < end && i >= (*p)->get_nauthors(); p++)
    1053             :       ;
    1054           0 :     if (p < end && i > 0) {
    1055             :       // If we have an author list <A B C> and an author list <A B C D>,
    1056             :       // then both lists need C.
    1057           0 :       for (reference **q = start; q < end; q++)
    1058           0 :         (*q)->need_author(i - 1);
    1059             :     }
    1060           0 :     start = p;
    1061             :   }
    1062           0 :   while (p < end) {
    1063           0 :     reference **last_name_start = p;
    1064           0 :     reference **name_start = p;
    1065           0 :     for (++p;
    1066           0 :          p < end && i < (*p)->get_nauthors()
    1067           0 :          && same_author_last_name(**last_name_start, **p, i);
    1068             :          p++) {
    1069           0 :       if (!same_author_name(**name_start, **p, i)) {
    1070           0 :         consider_authors(name_start, p, i + 1);
    1071           0 :         name_start = p;
    1072             :       }
    1073             :     }
    1074           0 :     consider_authors(name_start, p, i + 1);
    1075           0 :     if (last_name_start == name_start) {
    1076           0 :       for (reference **q = last_name_start; q < p; q++)
    1077           0 :         (*q)->set_last_name_unambiguous(i);
    1078             :     }
    1079             :     // If we have an author list <A B C D> and <A B C E>, then the lists
    1080             :     // need author D and E respectively.
    1081           0 :     if (name_start > start || p < end) {
    1082           0 :       for (reference **q = last_name_start; q < p; q++)
    1083           0 :         (*q)->need_author(i);
    1084             :     }
    1085             :   }
    1086             : }
    1087             : 
    1088           0 : int same_author_last_name(const reference &r1, const reference &r2, int n)
    1089             : {
    1090             :   const char *ae1;
    1091           0 :   const char *as1 = r1.get_sort_field(0, n, 0, &ae1);
    1092             :   const char *ae2;
    1093           0 :   const char *as2 = r2.get_sort_field(0, n, 0, &ae2);
    1094           0 :   if (!as1 && !as2) return 1;   // they are the same
    1095           0 :   if (!as1 || !as2) return 0;
    1096           0 :   return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0;
    1097             : }
    1098             : 
    1099           0 : int same_author_name(const reference &r1, const reference &r2, int n)
    1100             : {
    1101             :   const char *ae1;
    1102           0 :   const char *as1 = r1.get_sort_field(0, n, -1, &ae1);
    1103             :   const char *ae2;
    1104           0 :   const char *as2 = r2.get_sort_field(0, n, -1, &ae2);
    1105           0 :   if (!as1 && !as2) return 1;   // they are the same
    1106           0 :   if (!as1 || !as2) return 0;
    1107           0 :   return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0;
    1108             : }
    1109             : 
    1110             : 
    1111           0 : void int_set::set(int i)
    1112             : {
    1113           0 :   assert(i >= 0);
    1114           0 :   int bytei = i >> 3;
    1115           0 :   if (bytei >= v.length()) {
    1116           0 :     int old_length = v.length();
    1117           0 :     v.set_length(bytei + 1);
    1118           0 :     for (int j = old_length; j <= bytei; j++)
    1119           0 :       v[j] = 0;
    1120             :   }
    1121           0 :   v[bytei] |= 1 << (i & 7);
    1122           0 : }
    1123             : 
    1124           0 : int int_set::get(int i) const
    1125             : {
    1126           0 :   assert(i >= 0);
    1127           0 :   int bytei = i >> 3;
    1128           0 :   return bytei >= v.length() ? 0 : (v[bytei] & (1 << (i & 7))) != 0;
    1129             : }
    1130             : 
    1131           0 : void reference::set_last_name_unambiguous(int i)
    1132             : {
    1133           0 :   last_name_unambiguous.set(i);
    1134           0 : }
    1135             : 
    1136           0 : void reference::need_author(int n)
    1137             : {
    1138           0 :   if (n > last_needed_author)
    1139           0 :     last_needed_author = n;
    1140           0 : }
    1141             : 
    1142           0 : const char *reference::get_authors(const char **end) const
    1143             : {
    1144           0 :   if (!computed_authors) {
    1145           0 :     ((reference *)this)->computed_authors = 1;
    1146           0 :     string &result = ((reference *)this)->authors;
    1147           0 :     int na = get_nauthors();
    1148           0 :     result.clear();
    1149           0 :     for (int i = 0; i < na; i++) {
    1150           0 :       if (last_name_unambiguous.get(i)) {
    1151           0 :         const char *e, *start = get_author_last_name(i, &e);
    1152           0 :         assert(start != 0);
    1153           0 :         result.append(start, e - start);
    1154             :       }
    1155             :       else {
    1156           0 :         const char *e, *start = get_author(i, &e);
    1157           0 :         assert(start != 0);
    1158           0 :         result.append(start, e - start);
    1159             :       }
    1160           0 :       if (i == last_needed_author
    1161           0 :           && et_al.length() > 0
    1162           0 :           && et_al_min_elide > 0
    1163           0 :           && last_needed_author + et_al_min_elide < na
    1164           0 :           && na >= et_al_min_total) {
    1165           0 :         result += et_al;
    1166           0 :         break;
    1167             :       }
    1168           0 :       if (i < na - 1) {
    1169           0 :         if (na == 2)
    1170           0 :           result += join_authors_exactly_two;
    1171           0 :         else if (i < na - 2)
    1172           0 :           result += join_authors_default;
    1173             :         else
    1174           0 :           result += join_authors_last_two;
    1175             :       }
    1176             :     }
    1177             :   }
    1178           0 :   const char *start = authors.contents();
    1179           0 :   *end = start + authors.length();
    1180           0 :   return start;
    1181             : }
    1182             : 
    1183           0 : int reference::get_nauthors() const
    1184             : {
    1185           0 :   if (nauthors < 0) {
    1186             :     const char *dummy;
    1187             :     int na;
    1188           0 :     for (na = 0; get_author(na, &dummy) != 0; na++)
    1189             :       ;
    1190           0 :     ((reference *)this)->nauthors = na;
    1191             :   }
    1192           0 :   return nauthors;
    1193             : }
    1194             : 
    1195             : // Local Variables:
    1196             : // fill-column: 72
    1197             : // mode: C++
    1198             : // End:
    1199             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14