Code coverage tests

This page documents the degree to which the PARI/GP source code is tested by our public test suite, distributed with the source distribution in directory src/test/. This is measured by the gcov utility; we then process gcov output using the lcov frond-end.

We test a few variants depending on Configure flags on the pari.math.u-bordeaux1.fr machine (x86_64 architecture), and agregate them in the final report:

The target is 90% coverage for all mathematical modules (given that branches depending on DEBUGLEVEL or DEBUGMEM are not covered). This script is run to produce the results below.

LCOV - code coverage report
Current view: top level - gp - gp_rl.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.8.0 lcov report (development 16781-61614ca) Lines: 35 455 7.7 %
Date: 2014-09-14 Functions: 2 40 5.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 4 389 1.0 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright (C) 2000  The PARI group.
       2                 :            : 
       3                 :            : This file is part of the PARI/GP package.
       4                 :            : 
       5                 :            : PARI/GP is free software; you can redistribute it and/or modify it under the
       6                 :            : terms of the GNU General Public License as published by the Free Software
       7                 :            : Foundation. It is distributed in the hope that it will be useful, but WITHOUT
       8                 :            : ANY WARRANTY WHATSOEVER.
       9                 :            : 
      10                 :            : Check the License for details. You should have received a copy of it, along
      11                 :            : with the package; see the file 'COPYING'. If not, write to the Free Software
      12                 :            : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13                 :            : 
      14                 :            : /*******************************************************************/
      15                 :            : /*                                                                 */
      16                 :            : /*                 INTERFACE TO READLINE COMPLETION                */
      17                 :            : /*                                                                 */
      18                 :            : /*******************************************************************/
      19                 :            : #include "pari.h"
      20                 :            : 
      21                 :            : #ifdef READLINE
      22                 :            : 
      23                 :            : #include "paripriv.h"
      24                 :            : #include "gp.h"
      25                 :            : 
      26                 :            : typedef int (*RLCI)(int, int); /* rl_complete and rl_insert functions */
      27                 :            : typedef char* (*GF)(const char*, int); /* generator function */
      28                 :            : 
      29                 :            : BEGINEXTERN
      30                 :            : /* otherwise C++ compilers will choke on rl_message() prototype */
      31                 :            : #define USE_VARARGS
      32                 :            : #define PREFER_STDARG
      33                 :            : #include <readline/readline.h>
      34                 :            : #include <readline/history.h>
      35                 :            : ENDEXTERN
      36                 :            : 
      37                 :            : /**************************************************************************/
      38                 :            : 
      39                 :            : enum { DO_MATCHED_INSERT = 2, DO_ARGS_COMPLETE = 4 };
      40                 :            : static ulong readline_state = DO_ARGS_COMPLETE;
      41                 :            : static char *current_histfile = NULL;
      42                 :            : 
      43                 :            : static int pari_rl_back;
      44                 :            : static int did_init_matched = 0;
      45                 :            : static entree *current_ep = NULL;
      46                 :            : 
      47                 :            : static int
      48                 :          0 : change_state(const char *msg, ulong flag, int count)
      49                 :            : {
      50                 :          0 :   int c = (readline_state & flag) != 0;
      51                 :          0 :   ulong o_readline_state = readline_state;
      52                 :            : 
      53      [ #  #  # ]:          0 :   switch(count)
      54                 :            :   {
      55                 :          0 :     default: c = 0; break; /* off */
      56                 :          0 :     case -1: c = 1; break; /* on  */
      57                 :          0 :     case -2: c = 1 - c; /* toggle */
      58                 :            :   }
      59         [ #  # ]:          0 :   if (c)
      60                 :          0 :     readline_state |= flag;
      61                 :            :   else {
      62                 :          0 :     readline_state &= ~flag;
      63 [ #  # ][ #  # ]:          0 :     if (!readline_state && o_readline_state)
      64                 :          0 :         readline_state = 1;
      65                 :            :   }
      66                 :          0 :   rl_save_prompt();
      67         [ #  # ]:          0 :   rl_message("[%s: %s] ", msg, c? "on": "off");
      68                 :          0 :   c = rl_read_key();
      69                 :          0 :   rl_restore_prompt();
      70                 :          0 :   rl_clear_message();
      71                 :          0 :   rl_stuff_char(c); return 1;
      72                 :            : }
      73                 :            : 
      74                 :            : /* Wrapper around rl_complete to allow toggling insertion of arguments */
      75                 :            : static int
      76                 :          0 : pari_rl_complete(int count, int key)
      77                 :            : {
      78                 :            :   int ret;
      79                 :            : 
      80                 :          0 :   pari_rl_back = 0;
      81         [ #  # ]:          0 :   if (count <= 0)
      82                 :          0 :     return change_state("complete args", DO_ARGS_COMPLETE, count);
      83                 :            : 
      84                 :          0 :   rl_begin_undo_group();
      85         [ #  # ]:          0 :   if (rl_last_func == pari_rl_complete)
      86                 :          0 :     rl_last_func = (RLCI) rl_complete; /* Make repeated TABs different */
      87                 :          0 :   ret = ((RLCI)rl_complete)(count,key);
      88 [ #  # ][ #  # ]:          0 :   if (pari_rl_back && (pari_rl_back <= rl_point))
      89                 :          0 :     rl_point -= pari_rl_back;
      90                 :          0 :   rl_end_undo_group(); return ret;
      91                 :            : }
      92                 :            : 
      93                 :            : static int did_matched_insert;
      94                 :            : 
      95                 :            : static int
      96                 :          0 : pari_rl_matched_insert_suspend(int count, int key)
      97                 :            : {
      98                 :          0 :   ulong o_readline_state = readline_state;
      99                 :            :   (void)count; (void)key;
     100                 :            : 
     101                 :          0 :   did_matched_insert = (readline_state & DO_MATCHED_INSERT);
     102                 :          0 :   readline_state &= ~DO_MATCHED_INSERT;
     103 [ #  # ][ #  # ]:          0 :   if (!readline_state && o_readline_state)
     104                 :          0 :     readline_state = 1;
     105                 :          0 :   return 1;
     106                 :            : }
     107                 :            : 
     108                 :            : static int
     109                 :          0 : pari_rl_matched_insert_restore(int count, int key)
     110                 :            : {
     111                 :            :   (void)count; (void)key;
     112         [ #  # ]:          0 :   if (did_matched_insert)
     113                 :          0 :     readline_state |= DO_MATCHED_INSERT;
     114                 :          0 :   return 1;
     115                 :            : }
     116                 :            : 
     117                 :            : static const char paropen[] = "([{";
     118                 :            : static const char parclose[] = ")]}";
     119                 :            : 
     120                 :            : /* To allow insertion of () with a point in between. */
     121                 :            : static int
     122                 :          0 : pari_rl_matched_insert(int count, int key)
     123                 :            : {
     124                 :          0 :   int i = 0, ret;
     125                 :            : 
     126         [ #  # ]:          0 :   if (count <= 0)
     127                 :          0 :     return change_state("electric parens", DO_MATCHED_INSERT, count);
     128 [ #  # ][ #  # ]:          0 :   while (paropen[i] && paropen[i] != key) i++;
     129 [ #  # ][ #  # ]:          0 :   if (!paropen[i] || !(readline_state & DO_MATCHED_INSERT) || GP_DATA->flags & gpd_EMACS)
                 [ #  # ]
     130                 :          0 :     return ((RLCI)rl_insert)(count,key);
     131                 :          0 :   rl_begin_undo_group();
     132                 :          0 :   ((RLCI)rl_insert)(count,key);
     133                 :          0 :   ret = ((RLCI)rl_insert)(count,parclose[i]);
     134                 :          0 :   rl_point -= count;
     135                 :          0 :   rl_end_undo_group(); return ret;
     136                 :            : }
     137                 :            : 
     138                 :            : static int
     139                 :          0 : pari_rl_default_matched_insert(int count, int key)
     140                 :            : {
     141         [ #  # ]:          0 :     if (!did_init_matched) {
     142                 :          0 :       did_init_matched = 1;
     143                 :          0 :       readline_state |= DO_MATCHED_INSERT;
     144                 :            :     }
     145                 :          0 :     return pari_rl_matched_insert(count, key);
     146                 :            : }
     147                 :            : 
     148                 :            : static int
     149                 :          0 : pari_rl_forward_sexp(int count, int key)
     150                 :            : {
     151                 :          0 :   int deep = 0, dir = 1, move_point = 0, lfail;
     152                 :            : 
     153                 :            :   (void)key;
     154         [ #  # ]:          0 :   if (count < 0)
     155                 :            :   {
     156                 :          0 :     count = -count; dir = -1;
     157         [ #  # ]:          0 :     if (!rl_point) goto fail;
     158                 :          0 :     rl_point--;
     159                 :            :   }
     160 [ #  # ][ #  # ]:          0 :   while (count || deep)
     161                 :            :   {
     162                 :          0 :     move_point = 1;        /* Need to move point if moving left. */
     163                 :          0 :     lfail = 0;                /* Do not need to fail left movement yet. */
     164 [ #  # ][ #  # ]:          0 :     while ( !is_keyword_char(rl_line_buffer[rl_point])
     165         [ #  # ]:          0 :             && !strchr("\"([{}])",rl_line_buffer[rl_point])
     166 [ #  # ][ #  # ]:          0 :             && !( (dir == 1)
     167                 :            :                   ? (rl_point >= rl_end)
     168         [ #  # ]:          0 :                   : (rl_point <= 0 && (lfail = 1))))
     169                 :          0 :         rl_point += dir;
     170 [ #  # ][ #  # ]:          0 :     if (lfail || !rl_line_buffer[rl_point]) goto fail;
     171                 :            : 
     172         [ #  # ]:          0 :     if (is_keyword_char(rl_line_buffer[rl_point]))
     173                 :            :     {
     174 [ #  # ][ #  # ]:          0 :       while ( is_keyword_char(rl_line_buffer[rl_point])
     175 [ #  # ][ #  # ]:          0 :               && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0 && (lfail = 1)))
     176 [ #  # ][ #  # ]:          0 :                   || (move_point = 0)))
     177                 :          0 :         rl_point += dir;
     178 [ #  # ][ #  # ]:          0 :       if (deep && lfail) goto fail;
     179         [ #  # ]:          0 :       if (!deep) count--;
     180                 :            :     }
     181         [ #  # ]:          0 :     else if (strchr(paropen,rl_line_buffer[rl_point]))
     182                 :            :     {
     183 [ #  # ][ #  # ]:          0 :       if (deep == 0 && dir == -1) goto fail; /* We are already out of pars. */
     184                 :          0 :       rl_point += dir;
     185         [ #  # ]:          0 :       deep++; if (!deep) count--;
     186                 :            :     }
     187         [ #  # ]:          0 :     else if (strchr(parclose,rl_line_buffer[rl_point]))
     188                 :            :     {
     189 [ #  # ][ #  # ]:          0 :       if (deep == 0 && dir == 1)
     190                 :            :       {
     191                 :          0 :         rl_point++; goto fail; /* Get out of pars. */
     192                 :            :       }
     193                 :          0 :       rl_point += dir;
     194         [ #  # ]:          0 :       deep--; if (!deep) count--;
     195                 :            :     }
     196         [ #  # ]:          0 :     else if (rl_line_buffer[rl_point] == '\"')
     197                 :            :     {
     198                 :          0 :       int bad = 1;
     199                 :            : 
     200                 :          0 :       rl_point += dir;
     201 [ #  # ][ #  # ]:          0 :       while ( ((rl_line_buffer[rl_point] != '\"') || (bad = 0))
                 [ #  # ]
     202         [ #  # ]:          0 :               && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0))
     203 [ #  # ][ #  # ]:          0 :                   || (move_point = 0)) )
     204                 :          0 :         rl_point += dir;
     205         [ #  # ]:          0 :       if (bad) goto fail;
     206                 :          0 :       rl_point += dir;        /* Skip the other delimiter */
     207         [ #  # ]:          0 :       if (!deep) count--;
     208                 :            :     }
     209                 :            :     else
     210                 :            :     {
     211                 :          0 :       fail: rl_ding(); return 1;
     212                 :            :     }
     213                 :            :   }
     214 [ #  # ][ #  # ]:          0 :   if (dir != 1 && move_point) rl_point++;
     215                 :          0 :   return 1;
     216                 :            : }
     217                 :            : 
     218                 :            : static int
     219                 :          0 : pari_rl_backward_sexp(int count, int key)
     220                 :            : {
     221                 :          0 :   return pari_rl_forward_sexp(-count, key);
     222                 :            : }
     223                 :            : 
     224                 :            : /* do we add () at the end of completed word? (is it a function?) */
     225                 :            : static int
     226                 :          0 : add_paren(int end)
     227                 :            : {
     228                 :            :   entree *ep;
     229                 :            :   const char *s;
     230                 :            : 
     231 [ #  # ][ #  # ]:          0 :   if (end < 0 || rl_line_buffer[end] == '(')
     232                 :          0 :     return 0; /* not from command_generator or already there */
     233                 :          0 :   ep = do_alias(current_ep); /* current_ep set in command_generator */
     234         [ #  # ]:          0 :   if (EpVALENCE(ep) < EpNEW)
     235                 :            :   { /* is it a constant masked as a function (e.g Pi)? */
     236         [ #  # ]:          0 :     s = ep->help; if (!s) return 1;
     237         [ #  # ]:          0 :     while (is_keyword_char(*s)) s++;
     238                 :          0 :     return (*s != '=');
     239                 :            :   }
     240      [ #  #  # ]:          0 :   switch(EpVALENCE(ep))
     241                 :            :   {
     242                 :          0 :     case EpVAR: return typ((GEN)ep->value) == t_CLOSURE;
     243                 :          0 :     case EpINSTALL: return 1;
     244                 :            :   }
     245                 :          0 :   return 0;
     246                 :            : }
     247                 :            : 
     248                 :            : static void
     249                 :          0 : match_concat(char **matches, const char *s)
     250                 :            : {
     251                 :          0 :   matches[0] = (char*)pari_realloc((void*)matches[0],
     252                 :          0 :                                 strlen(matches[0])+strlen(s)+1);
     253                 :          0 :   strcat(matches[0],s);
     254                 :          0 : }
     255                 :            : 
     256                 :            : #define add_comma(x) (x==-2) /* from default_generator */
     257                 :            : 
     258                 :            : /* a single match, possibly modify matches[0] in place */
     259                 :            : static void
     260                 :          0 : treat_single(int code, char **matches)
     261                 :            : {
     262         [ #  # ]:          0 :   if (add_paren(code))
     263                 :            :   {
     264                 :          0 :     match_concat(matches,"()");
     265                 :          0 :     pari_rl_back = 1;
     266         [ #  # ]:          0 :     if (rl_point == rl_end)
     267                 :          0 :     rl_completion_append_character = '\0'; /* Do not append space. */
     268                 :            :   }
     269         [ #  # ]:          0 :   else if (add_comma(code))
     270                 :          0 :     match_concat(matches,",");
     271                 :          0 : }
     272                 :            : #undef add_comma
     273                 :            : 
     274                 :            : 
     275                 :            : static char **
     276                 :          0 : matches_for_emacs(const char *text, char **matches)
     277                 :            : {
     278         [ #  # ]:          0 :   if (!matches) printf("@");
     279                 :            :   else
     280                 :            :   {
     281                 :            :     int i;
     282                 :          0 :     printf("%s@", matches[0] + strlen(text));
     283         [ #  # ]:          0 :     if (matches[1]) print_fun_list(matches+1,0);
     284                 :            : 
     285                 :            :    /* we don't want readline to do anything, but insert some junk
     286                 :            :     * which will be erased by emacs.
     287                 :            :     */
     288         [ #  # ]:          0 :     for (i=0; matches[i]; i++) pari_free(matches[i]);
     289                 :          0 :     pari_free(matches);
     290                 :            :   }
     291                 :          0 :   matches = (char **) pari_malloc(2*sizeof(char *));
     292                 :          0 :   matches[0] = (char*)pari_malloc(2); sprintf(matches[0],"_");
     293                 :          0 :   matches[1] = NULL; printf("@E_N_D"); pari_flush();
     294                 :          0 :   return matches;
     295                 :            : }
     296                 :            : 
     297                 :            : /* Attempt to complete on the contents of TEXT. 'code' is used to
     298                 :            :  * differentiate between callers when a single match is found.
     299                 :            :  * Return the array of matches, NULL if there are none. */
     300                 :            : static char **
     301                 :          0 : get_matches(int code, const char *text, GF f)
     302                 :            : {
     303                 :          0 :   char **matches = rl_completion_matches(text, f);
     304 [ #  # ][ #  # ]:          0 :   if (matches && !matches[1]) treat_single(code, matches);
     305         [ #  # ]:          0 :   if (GP_DATA->flags & gpd_EMACS) matches = matches_for_emacs(text,matches);
     306                 :          0 :   return matches;
     307                 :            : }
     308                 :            : 
     309                 :            : static char *
     310                 :          0 : generator(void *list, const char *text, int *nn, int len)
     311                 :            : {
     312                 :          0 :   const char *def = NULL;
     313                 :          0 :   int n = *nn;
     314                 :            : 
     315                 :            :   /* Return the next name which partially matches from list.*/
     316                 :            :   do
     317                 :          0 :     def = (((entree *) list)[n++]).name;
     318 [ #  # ][ #  # ]:          0 :   while (def && strncmp(def,text,len));
     319                 :          0 :   *nn = n;
     320         [ #  # ]:          0 :   if (def)
     321                 :            :   {
     322                 :          0 :     char *name = strcpy((char*)pari_malloc(strlen(def)+1), def);
     323                 :          0 :     return name;
     324                 :            :   }
     325                 :          0 :   return NULL; /* no names matched */
     326                 :            : }
     327                 :            : static char *
     328                 :          0 : old_generator(const char *text,int state)
     329                 :            : {
     330                 :            :   static int n,len;
     331                 :            :   static char *res;
     332                 :            : 
     333         [ #  # ]:          0 :   if (!state) { res = (char*)"a"; n=0; len=strlen(text); }
     334         [ #  # ]:          0 :   if (res)
     335                 :            :   {
     336                 :          0 :     res = generator((void *)oldfonctions,text,&n,len);
     337         [ #  # ]:          0 :     if (res) return res;
     338                 :          0 :     n=0;
     339                 :            :   }
     340                 :          0 :   return generator((void *)functions_oldgp,text,&n,len);
     341                 :            : }
     342                 :            : static char *
     343                 :          0 : add_prefix(const char *name, const char *text, long junk)
     344                 :            : {
     345                 :          0 :   char *s = strncpy((char*)pari_malloc(strlen(name)+1+junk),text,junk);
     346                 :          0 :   strcpy(s+junk,name); return s;
     347                 :            : }
     348                 :            : static void
     349                 :          0 : init_prefix(const char *text, int *len, int *junk, char **TEXT)
     350                 :            : {
     351                 :          0 :   long l = strlen(text), j = l-1;
     352 [ #  # ][ #  # ]:          0 :   while (j >= 0 && is_keyword_char(text[j])) j--;
     353                 :          0 :   j++;
     354                 :          0 :   *TEXT = (char*)text + j;
     355                 :          0 :   *junk = j;
     356                 :          0 :   *len  = l - j;
     357                 :          0 : }
     358                 :            : static int
     359 [ #  # ][ #  # ]:          0 : is_internal(entree *ep) { return ep->menu >= 13 && ep->menu <= 15; }
     360                 :            : 
     361                 :            : /* Generator function for command completion.  STATE lets us know whether
     362                 :            :  * to start from scratch; without any state (i.e. STATE == 0), then we
     363                 :            :  * start at the top of the list. */
     364                 :            : static char *
     365                 :          0 : hashtable_generator(const char *text, int state, entree **hash)
     366                 :            : {
     367                 :            :   static int hashpos, len, junk;
     368                 :            :   static entree* ep;
     369                 :            :   static char *TEXT;
     370                 :            : 
     371                 :            :  /* If this is a new word to complete, initialize now:
     372                 :            :   *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
     373                 :            :   *  + file completion and keyword completion use different word boundaries,
     374                 :            :   *    have TEXT point to the keyword start.
     375                 :            :   *  + save the length of TEXT for efficiency.
     376                 :            :   */
     377         [ #  # ]:          0 :   if (!state)
     378                 :            :   {
     379                 :          0 :     hashpos = 0; ep = hash[hashpos];
     380                 :          0 :     init_prefix(text, &len, &junk, &TEXT);
     381                 :            :   }
     382                 :            : 
     383                 :            :   /* Return the next name which partially matches from the command list. */
     384                 :            :   for(;;)
     385         [ #  # ]:          0 :     if (!ep)
     386                 :            :     {
     387         [ #  # ]:          0 :       if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
     388                 :          0 :       ep = hash[hashpos];
     389                 :            :     }
     390 [ #  # ][ #  # ]:          0 :     else if (is_internal(ep) || strncmp(ep->name,TEXT,len))
     391                 :          0 :       ep = ep->next;
     392                 :            :     else
     393                 :          0 :       break;
     394                 :          0 :   current_ep = ep; ep = ep->next;
     395                 :          0 :   return add_prefix(current_ep->name,text,junk);
     396                 :            : }
     397                 :            : /* Generator function for member completion.  STATE lets us know whether
     398                 :            :  * to start from scratch; without any state (i.e. STATE == 0), then we
     399                 :            :  * start at the top of the list. */
     400                 :            : static char *
     401                 :          0 : member_generator(const char *text, int state)
     402                 :            : {
     403                 :            :   static int hashpos, len, junk;
     404                 :            :   static entree* ep;
     405                 :            :   static char *TEXT;
     406                 :          0 :   entree **hash=functions_hash;
     407                 :            : 
     408                 :            :  /* If this is a new word to complete, initialize now:
     409                 :            :   *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
     410                 :            :   *  + file completion and keyword completion use different word boundaries,
     411                 :            :   *    have TEXT point to the keyword start.
     412                 :            :   *  + save the length of TEXT for efficiency.
     413                 :            :   */
     414         [ #  # ]:          0 :   if (!state)
     415                 :            :   {
     416                 :          0 :     hashpos = 0; ep = hash[hashpos];
     417                 :          0 :     init_prefix(text, &len, &junk, &TEXT);
     418                 :            :   }
     419                 :            : 
     420                 :            :   /* Return the next name which partially matches from the command list. */
     421                 :            :   for(;;)
     422         [ #  # ]:          0 :     if (!ep)
     423                 :            :     {
     424         [ #  # ]:          0 :       if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
     425                 :          0 :       ep = hash[hashpos];
     426                 :            :     }
     427 [ #  # ][ #  # ]:          0 :     else if (ep->name[0]=='_' && ep->name[1]=='.'
     428         [ #  # ]:          0 :              && !strncmp(ep->name+2,TEXT,len))
     429                 :            :         break;
     430                 :            :     else
     431                 :          0 :         ep = ep->next;
     432                 :          0 :   current_ep = ep; ep = ep->next;
     433                 :          0 :   return add_prefix(current_ep->name+2,text,junk);
     434                 :            : }
     435                 :            : static char *
     436                 :          0 : command_generator(const char *text, int state)
     437                 :          0 : { return hashtable_generator(text,state, functions_hash); }
     438                 :            : static char *
     439                 :          0 : default_generator(const char *text,int state)
     440                 :          0 : { return hashtable_generator(text,state, defaults_hash); }
     441                 :            : 
     442                 :            : static char *
     443                 :          0 : ext_help_generator(const char *text, int state)
     444                 :            : {
     445                 :            :   static int len, junk, n, def, key;
     446                 :            :   static char *TEXT;
     447         [ #  # ]:          0 :   if (!state) {
     448                 :          0 :     n = 0;
     449                 :          0 :     def = key = 1;
     450                 :          0 :     init_prefix(text, &len, &junk, &TEXT);
     451                 :            :   }
     452         [ #  # ]:          0 :   if (def)
     453                 :            :   {
     454                 :          0 :     char *s = default_generator(TEXT, state);
     455         [ #  # ]:          0 :     if (s) return add_prefix(s, text, junk);
     456                 :          0 :     def = 0;
     457                 :            :   }
     458         [ #  # ]:          0 :   if (key)
     459                 :            :   {
     460         [ #  # ]:          0 :     for ( ; keyword_list[n]; n++)
     461         [ #  # ]:          0 :       if (!strncmp(keyword_list[n],TEXT,len))
     462                 :          0 :         return add_prefix(keyword_list[n++], text, junk);
     463                 :          0 :     key = 0; state = 0;
     464                 :            :   }
     465                 :          0 :   return command_generator(text, state);
     466                 :            : }
     467                 :            : 
     468                 :            : static void
     469                 :          0 : rl_print_aide(char *s, int flag)
     470                 :            : {
     471                 :          0 :   int p = rl_point, e = rl_end;
     472                 :          0 :   FILE *save = pari_outfile;
     473                 :            : 
     474                 :          0 :   rl_point = 0; rl_end = 0; pari_outfile = rl_outstream;
     475                 :          0 :   rl_save_prompt();
     476                 :          0 :   rl_message("%s",""); /* rl_message("") ==> "zero length format" warning */
     477                 :          0 :   aide(s, flag);
     478                 :          0 :   rl_restore_prompt();
     479                 :          0 :   rl_point = p; rl_end = e; pari_outfile = save;
     480                 :          0 :   rl_clear_message();
     481                 :          0 :   rl_refresh_line(0,0);
     482                 :          0 : }
     483                 :            : 
     484                 :            : /* add a space between \<char> and following text. Attempting completion now
     485                 :            :  * would delete char. Hitting <TAB> again will complete properly */
     486                 :            : static char **
     487                 :          0 : add_space(int start)
     488                 :            : {
     489                 :            :   char **m;
     490                 :          0 :   int p = rl_point + 1;
     491                 :          0 :   rl_point = start + 2;
     492                 :          0 :   rl_insert(1, ' '); rl_point = p;
     493                 :            :   /*better: fake an empty completion, but don't append ' ' after it! */
     494                 :          0 :   rl_completion_append_character = '\0';
     495                 :          0 :   m = (char**)pari_malloc(2 * sizeof(char*));
     496                 :          0 :   m[0] = (char*)pari_malloc(1); *(m[0]) = 0;
     497                 :          0 :   m[1] = NULL; return m;
     498                 :            : }
     499                 :            : 
     500                 :            : static char **
     501                 :          0 : pari_completion(char *text, int START, int END)
     502                 :            : {
     503                 :          0 :   int i, first=0, start=START;
     504                 :            : 
     505                 :          0 :   rl_completion_append_character = ' ';
     506                 :          0 :   current_ep = NULL;
     507                 :            : /* If the line does not begin by a backslash, then it is:
     508                 :            :  * . an old command ( if preceded by "whatnow(" ).
     509                 :            :  * . a default ( if preceded by "default(" ).
     510                 :            :  * . a member function ( if preceded by "." + keyword_chars )
     511                 :            :  * . a file name (in current directory) ( if preceded by 'read' or 'writexx' )
     512                 :            :  * . a command */
     513 [ #  # ][ #  # ]:          0 :   if (start >=1 && rl_line_buffer[start] != '~') start--;
     514 [ #  # ][ #  # ]:          0 :   while (start && is_keyword_char(rl_line_buffer[start])) start--;
     515         [ #  # ]:          0 :   if (rl_line_buffer[start] == '~')
     516                 :            :   {
     517                 :          0 :     GF f = (GF)rl_username_completion_function;
     518         [ #  # ]:          0 :     for(i=start+1;i<=END;i++)
     519         [ #  # ]:          0 :       if (rl_line_buffer[i] == '/') { f = (GF)rl_filename_completion_function; break; }
     520                 :          0 :     return get_matches(-1, text, f);
     521                 :            :   }
     522                 :            : 
     523 [ #  # ][ #  # ]:          0 :   while (rl_line_buffer[first] && isspace((int)rl_line_buffer[first])) first++;
     524      [ #  #  # ]:          0 :   switch (rl_line_buffer[first])
     525                 :            :   {
     526                 :            :     case '\\':
     527         [ #  # ]:          0 :       if (first == start) return add_space(start);
     528                 :          0 :       return get_matches(-1, text, rl_filename_completion_function);
     529                 :            :     case '?':
     530         [ #  # ]:          0 :       if (rl_line_buffer[first+1] == '?')
     531                 :          0 :         return get_matches(-1, text, ext_help_generator);
     532                 :          0 :       return get_matches(-1, text, command_generator);
     533                 :            :   }
     534                 :            : 
     535 [ #  # ][ #  # ]:          0 :   while (start && rl_line_buffer[start] != '('
     536         [ #  # ]:          0 :                && rl_line_buffer[start] != ',') start--;
     537 [ #  # ][ #  # ]:          0 :   if (rl_line_buffer[start] == '(' && start)
     538                 :            :   {
     539                 :            :     int iend, j,k;
     540                 :            :     entree *ep;
     541                 :            :     char buf[200];
     542                 :            : 
     543                 :          0 :     i = start;
     544                 :            : 
     545 [ #  # ][ #  # ]:          0 :     while (i && isspace((int)rl_line_buffer[i-1])) i--;
     546                 :          0 :     iend = i;
     547 [ #  # ][ #  # ]:          0 :     while (i && is_keyword_char(rl_line_buffer[i-1])) i--;
     548                 :            : 
     549         [ #  # ]:          0 :     if (strncmp(rl_line_buffer + i,"default",7) == 0)
     550                 :          0 :       return get_matches(-2, text, default_generator);
     551         [ #  # ]:          0 :     if (strncmp(rl_line_buffer + i,"whatnow",7) == 0)
     552                 :          0 :       return get_matches(-1, text, old_generator);
     553         [ #  # ]:          0 :     if ( strncmp(rl_line_buffer + i,"read",4)  == 0
     554         [ #  # ]:          0 :       || strncmp(rl_line_buffer + i,"write",5) == 0)
     555                 :          0 :       return get_matches(-1, text, rl_filename_completion_function);
     556                 :            : 
     557                 :          0 :     j = start + 1;
     558 [ #  # ][ #  # ]:          0 :     while (j <= END && isspace((int)rl_line_buffer[j])) j++;
     559                 :          0 :     k = END;
     560 [ #  # ][ #  # ]:          0 :     while (k > j && isspace((int)rl_line_buffer[k])) k--;
     561                 :            :     /* If we are in empty parens, insert the default arguments */
     562 [ #  # ][ #  # ]:          0 :     if ((readline_state & DO_ARGS_COMPLETE) && k == j
     563 [ #  # ][ #  # ]:          0 :          && (rl_line_buffer[j] == ')' || !rl_line_buffer[j])
     564         [ #  # ]:          0 :          && (iend - i < (long)sizeof(buf))
     565                 :          0 :          && ( strncpy(buf, rl_line_buffer + i, iend - i),
     566                 :          0 :               buf[iend - i] = 0, 1)
     567 [ #  # ][ #  # ]:          0 :          && (ep = is_entry(buf)) && ep->help)
     568                 :            :     {
     569                 :          0 :       const char *s = ep->help;
     570         [ #  # ]:          0 :       while (is_keyword_char(*s)) s++;
     571         [ #  # ]:          0 :       if (*s++ == '(')
     572                 :            :       { /* function call: insert arguments */
     573                 :          0 :         const char *e = s;
     574 [ #  # ][ #  # ]:          0 :         while (*e && *e != ')' && *e != '(') e++;
                 [ #  # ]
     575         [ #  # ]:          0 :         if (*e == ')')
     576                 :            :         { /* we just skipped over the arguments in short help text */
     577                 :          0 :           char *str = strncpy((char*)pari_malloc(e-s + 1), s, e-s);
     578                 :          0 :           char **ret = (char**)pari_malloc(sizeof(char*)*2);
     579                 :          0 :           str[e-s] = 0;
     580                 :          0 :           ret[0] = str; ret[1] = NULL;
     581         [ #  # ]:          0 :           if (GP_DATA->flags & gpd_EMACS) ret = matches_for_emacs("",ret);
     582                 :          0 :           return ret;
     583                 :            :         }
     584                 :            :       }
     585                 :            :     }
     586                 :            :   }
     587         [ #  # ]:          0 :   for(i = END-1; i >= start; i--)
     588         [ #  # ]:          0 :     if (!is_keyword_char(rl_line_buffer[i]))
     589                 :            :     {
     590         [ #  # ]:          0 :       if (rl_line_buffer[i] == '.')
     591                 :          0 :         return get_matches(-1, text, member_generator);
     592                 :          0 :       break;
     593                 :            :     }
     594                 :          0 :   return get_matches(END, text, command_generator);
     595                 :            : }
     596                 :            : 
     597                 :            : /* long help if count < 0 */
     598                 :            : static int
     599                 :          0 : rl_short_help(int count, int key)
     600                 :            : {
     601                 :          0 :   int flag = h_RL;
     602                 :          0 :   char *s = rl_line_buffer + rl_point;
     603                 :            : 
     604                 :            :   (void)key;
     605                 :            :   /* func() with cursor on ')', e.g. following completion */
     606 [ #  # ][ #  # ]:          0 :   if (s > rl_line_buffer && *s == ')' && s[-1] == '(') s--;
                 [ #  # ]
     607 [ #  # ][ #  # ]:          0 :   while (s > rl_line_buffer && is_keyword_char(s[-1])) s--;
     608                 :            :   /* check for '\c' */
     609 [ #  # ][ #  # ]:          0 :   if (s > rl_line_buffer && s[-1] == '\\') s--;
     610                 :            : 
     611 [ #  # ][ #  # ]:          0 :   if (count < 0 || rl_last_func == rl_short_help) flag |= h_LONG;
     612                 :          0 :   rl_print_aide(s, flag); return 0;
     613                 :            : }
     614                 :            : 
     615                 :            : static int
     616                 :          0 : rl_long_help(int count, int key) { (void)count; return rl_short_help(-1,key); }
     617                 :            : 
     618                 :            : static void
     619                 :       1194 : init_histfile(void)
     620                 :            : {
     621 [ -  + ][ #  # ]:       1194 :   if (current_histfile && read_history(current_histfile))
     622                 :          0 :     write_history(current_histfile);
     623                 :       1194 : }
     624                 :            : 
     625                 :            : void
     626                 :       1194 : init_readline(void)
     627                 :            : {
     628                 :            :   static int init_done = 0;
     629                 :            : 
     630         [ -  + ]:       2388 :   if (init_done) return;
     631         [ +  - ]:       1194 :   if (! GP_DATA->use_readline) readline_state = 0;
     632                 :       1194 :   init_done = 1;
     633                 :       1194 :   init_histfile();
     634                 :            : 
     635                 :            :   /* Allow conditional parsing of the ~/.inputrc file. */
     636                 :       1194 :   rl_readline_name = "Pari-GP";
     637                 :            : 
     638                 :            :   /* added ~, ? and , */
     639                 :       1194 :   rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(?~";
     640                 :       1194 :   rl_special_prefixes = "~";
     641                 :            : 
     642                 :            :   /* custom completer */
     643                 :       1194 :   rl_attempted_completion_function = (rl_completion_func_t*) pari_completion;
     644                 :            : 
     645                 :            :   /* we always want the whole list of completions under emacs */
     646         [ -  + ]:       1194 :   if (GP_DATA->flags & gpd_EMACS) rl_completion_query_items = 0x8fff;
     647                 :            : 
     648                 :       1194 :   rl_add_defun("short-help", rl_short_help, -1);
     649                 :       1194 :   rl_add_defun("long-help", rl_long_help, -1);
     650                 :       1194 :   rl_add_defun("pari-complete", pari_rl_complete, '\t');
     651                 :       1194 :   rl_add_defun("pari-matched-insert", pari_rl_default_matched_insert, -1);
     652                 :       1194 :   rl_add_defun("pari-matched-insert-suspend", pari_rl_matched_insert_suspend, -1);
     653                 :       1194 :   rl_add_defun("pari-matched-insert-restore", pari_rl_matched_insert_restore, -1);
     654                 :       1194 :   rl_add_defun("pari-forward-sexp", pari_rl_forward_sexp, -1);
     655                 :       1194 :   rl_add_defun("pari-backward-sexp", pari_rl_backward_sexp, -1);
     656                 :            : 
     657                 :       1194 :   rl_bind_key_in_map('h', rl_short_help, emacs_meta_keymap);
     658                 :       1194 :   rl_bind_key_in_map('H', rl_long_help,  emacs_meta_keymap);
     659                 :            : 
     660                 :            : #define KSbind(s,f,k) rl_generic_bind(ISFUNC, (s), (char*)(f), (k))
     661                 :            : 
     662                 :       1194 :   KSbind("OP",   rl_short_help,  emacs_meta_keymap); /* f1, vt100 */
     663                 :       1194 :   KSbind("[11~", rl_short_help,  emacs_meta_keymap); /* f1, xterm */
     664                 :       1194 :   KSbind("OP",   rl_short_help,  vi_movement_keymap); /* f1, vt100 */
     665                 :       1194 :   KSbind("[11~", rl_short_help,  vi_movement_keymap); /* f1, xterm */
     666                 :            :   /* XTerm may signal start/end of paste by emitting F200/F201
     667                 :            :    * TODO: check to what extent this patch has been applied */
     668                 :            :   /* FIXME: For vi mode something more intelligent is needed - to switch to the
     669                 :            :      insert mode - and back when restoring. */
     670                 :       1194 :   KSbind("[200~", pari_rl_matched_insert_suspend,  emacs_meta_keymap);  /* pre-paste xterm */
     671                 :       1194 :   KSbind("[200~", pari_rl_matched_insert_suspend,  vi_movement_keymap); /* pre-paste xterm */
     672                 :       1194 :   KSbind("[201~", pari_rl_matched_insert_restore,  emacs_meta_keymap);  /* post-paste xterm */
     673                 :       1194 :   KSbind("[201~", pari_rl_matched_insert_restore,  vi_movement_keymap); /* post-paste xterm */
     674                 :       1194 :   rl_bind_key_in_map('(', pari_rl_matched_insert, emacs_standard_keymap);
     675                 :       1194 :   rl_bind_key_in_map('[', pari_rl_matched_insert, emacs_standard_keymap);
     676                 :       1194 :   rl_bind_key_in_map(6, pari_rl_forward_sexp,  emacs_meta_keymap); /* M-C-f */
     677                 :       1194 :   rl_bind_key_in_map(2, pari_rl_backward_sexp, emacs_meta_keymap); /* M-C-b */
     678                 :            : }
     679                 :            : 
     680                 :            : void
     681                 :          0 : readline_prompt_color(char *s, int c)
     682                 :            : {
     683                 :            : #ifdef RL_PROMPT_START_IGNORE
     684                 :          0 :   *s++ = RL_PROMPT_START_IGNORE;
     685                 :          0 :   term_get_color(s, c);
     686                 :          0 :   s += strlen(s);
     687                 :          0 :   *s++ = RL_PROMPT_END_IGNORE;
     688                 :          0 :   *s = 0;
     689                 :            : #else
     690                 :            :   term_get_color(s, c);
     691                 :            : #endif
     692                 :          0 : }
     693                 :            : 
     694                 :            : /* readline-specific defaults */
     695                 :            : GEN
     696                 :          0 : sd_readline(const char *v, long flag)
     697                 :            : {
     698                 :          0 :   const char *msg[] = {
     699                 :            :     "(bits 0x2/0x4 control matched-insert/arg-complete)", NULL};
     700                 :          0 :   ulong o_readline_state = readline_state;
     701                 :          0 :   GEN res = sd_ulong(v,flag,"readline", &readline_state, 0, 7, msg);
     702                 :            : 
     703         [ #  # ]:          0 :   if (o_readline_state != readline_state)
     704         [ #  # ]:          0 :     (void)sd_toggle(readline_state? "1": "0", d_SILENT, "readline", &(GP_DATA->use_readline));
     705                 :          0 :   return res;
     706                 :            : }
     707                 :            : GEN
     708                 :          0 : sd_histfile(const char *v, long flag)
     709                 :            : {
     710                 :          0 :   char *old = current_histfile;
     711                 :          0 :   GEN r = sd_string(v, flag, "histfile", &current_histfile);
     712 [ #  # ][ #  # ]:          0 :   if (v && !*v)
     713                 :            :   {
     714                 :          0 :     free(current_histfile);
     715                 :          0 :     current_histfile = NULL;
     716                 :            :   }
     717 [ #  # ][ #  # ]:          0 :   else if (current_histfile != old && (!old || strcmp(old,current_histfile)))
                 [ #  # ]
     718                 :          0 :     init_histfile();
     719                 :          0 :   return r;
     720                 :            : }
     721                 :            : 
     722                 :            : static void
     723                 :          0 : print_escape_string(char *s)
     724                 :            : {
     725                 :          0 :   long l = strlen(s);
     726                 :          0 :   char *t, *t0 = (char*)pari_malloc(l * 3 + 3);
     727                 :            : 
     728                 :          0 :   t = t0; *t++ = '"';
     729         [ #  # ]:          0 :   for ( ;*s; *t++ = *s++)
     730      [ #  #  # ]:          0 :     switch(*s)
     731                 :            :     {
     732                 :            :       case DATA_BEGIN:
     733                 :            :       case DATA_END:
     734                 :          0 :       case DATA_ESCAPE: *t++ = DATA_ESCAPE; continue;
     735                 :            : 
     736                 :            :       case '\\':
     737                 :          0 :       case '"': *t++ = '\\'; continue;
     738                 :            :     }
     739                 :          0 :   *t++ = '"';
     740                 :          0 :   *t = '\0'; puts(t0); pari_free(t0);
     741                 :          0 : }
     742                 :            : 
     743                 :            : static char *
     744                 :          0 : completion_word(long end)
     745                 :            : {
     746                 :          0 :   char *s = rl_line_buffer + end, *found_quote = NULL;
     747                 :            :   long i;
     748                 :            :   /* truncate at cursor position */
     749                 :          0 :   *s = 0;
     750                 :            :   /* first look for unclosed string */
     751         [ #  # ]:          0 :   for (i=0; i < end; i++)
     752                 :            :   {
     753      [ #  #  # ]:          0 :     switch(rl_line_buffer[i])
     754                 :            :     {
     755                 :            :       case '"':
     756         [ #  # ]:          0 :         found_quote = found_quote? NULL: rl_line_buffer + i;
     757                 :          0 :         break;
     758                 :            : 
     759                 :          0 :       case '\\': i++; break;
     760                 :            :     }
     761                 :            : 
     762                 :            :   }
     763         [ #  # ]:          0 :   if (found_quote) return found_quote + 1; /* return next char after quote */
     764                 :            : 
     765                 :            :   /* else find beginning of word */
     766         [ #  # ]:          0 :   while (s >  rl_line_buffer)
     767                 :            :   {
     768                 :          0 :     s--;
     769         [ #  # ]:          0 :     if (!is_keyword_char(*s)) { s++; break; }
     770                 :            :   }
     771                 :          0 :   return s;
     772                 :            : }
     773                 :            : 
     774                 :            : /* completion required, cursor on s + pos. Complete wrt strict left prefix */
     775                 :            : void
     776                 :          0 : texmacs_completion(const char *s, long pos)
     777                 :            : {
     778                 :            :   char **matches, *text;
     779                 :            : 
     780         [ #  # ]:          0 :   if (rl_line_buffer) pari_free(rl_line_buffer);
     781                 :          0 :   rl_line_buffer = pari_strdup(s);
     782                 :          0 :   text = completion_word(pos);
     783                 :            :   /* text = start of expression we complete */
     784                 :          0 :   rl_end = strlen(s)-1;
     785                 :          0 :   rl_point = pos;
     786                 :          0 :   matches = pari_completion(text, text - rl_line_buffer, pos);
     787                 :          0 :   printf("%cscheme:(tuple",DATA_BEGIN);
     788         [ #  # ]:          0 :   if (matches)
     789                 :            :   {
     790                 :          0 :     long i, prelen = (rl_line_buffer+pos) - text;
     791                 :          0 :     char *t = (char*)pari_malloc(prelen+1);
     792                 :          0 :     strncpy(t, text, prelen); t[prelen] = 0; /* prefix */
     793                 :          0 :     printf(" ");
     794                 :          0 :     print_escape_string(t); pari_free(t);
     795         [ #  # ]:          0 :     for (i = matches[1]? 1: 0; matches[i]; i++)
     796                 :            :     {
     797                 :          0 :       printf(" ");
     798                 :          0 :       print_escape_string(matches[i] + prelen);
     799                 :          0 :       pari_free(matches[i]);
     800                 :            :     }
     801                 :          0 :     pari_free(matches);
     802                 :            :   }
     803                 :          0 :   printf(")%c", DATA_END);
     804                 :          0 :   fflush(stdout);
     805                 :          0 : }
     806                 :            : 
     807                 :            : static int
     808                 :          0 : history_is_new(char *s)
     809                 :            : {
     810                 :            :   HIST_ENTRY *e;
     811         [ #  # ]:          0 :   if (!*s) return 0;
     812         [ #  # ]:          0 :   if (!history_length) return 1;
     813                 :          0 :   e = history_get(history_length);
     814                 :            :   /* paranoia: e != NULL, unless readline is in a weird state */
     815         [ #  # ]:          0 :   return e? strcmp(s, e->line): 0;
     816                 :            : }
     817                 :            : 
     818                 :            : static void
     819                 :          0 : gp_add_history(char *s)
     820                 :            : {
     821         [ #  # ]:          0 :   if (history_is_new(s)) { add_history(s); append_history(1,current_histfile); }
     822                 :          0 : }
     823                 :            : 
     824                 :            : /* Read line; returns a malloc()ed string of the user input or NULL on EOF.
     825                 :            :    Increments the buffer size appropriately if needed; fix *endp if so. */
     826                 :            : static char *
     827                 :          0 : gprl_input(char **endp, int first, input_method *IM, filtre_t *F)
     828                 :            : {
     829                 :            :   char buf[MAX_PROMPT_LEN + 24];
     830                 :          0 :   Buffer *b = F->buf;
     831                 :          0 :   ulong used = *endp - b->buf;
     832                 :          0 :   ulong left = b->len - used, l;
     833                 :          0 :   const char *prompt = first? IM->prompt
     834         [ #  # ]:          0 :                             : do_prompt(buf, IM->prompt_cont, F);
     835                 :            :   char *s, *t;
     836                 :            : 
     837         [ #  # ]:          0 :   if (! (s = readline(prompt)) ) return NULL; /* EOF */
     838                 :          0 :   gp_add_history(s); /* Makes a copy */
     839                 :          0 :   l = strlen(s) + 1;
     840                 :            :   /* put back \n that readline stripped. This is needed for
     841                 :            :    * { print("a
     842                 :            :    *   b"); }
     843                 :            :    * and conforms with the other input methods anyway. */
     844                 :          0 :   t = (char*)pari_malloc(l + 1);
     845                 :          0 :   strncpy(t, s, l-1);
     846                 :          0 :   t[l-1] = '\n';
     847                 :          0 :   t[l]   = 0; /* equivalent to sprintf(t,"%s\n", s) */
     848         [ #  # ]:          0 :   if (left < l)
     849                 :            :   {
     850                 :          0 :     ulong incr = b->len;
     851         [ #  # ]:          0 :     if (incr < l) incr = l;
     852                 :          0 :     fix_buffer(b, b->len + incr);
     853                 :          0 :     *endp = b->buf + used;
     854                 :            :   }
     855                 :          0 :   return t;
     856                 :            : }
     857                 :            : 
     858                 :            : /* request one line interactively.
     859                 :            :  * Return 0: EOF
     860                 :            :  *        1: got one line from readline or pari_infile */
     861                 :            : int
     862                 :          0 : get_line_from_readline(const char *prompt, const char *prompt_cont, filtre_t *F)
     863                 :            : {
     864                 :          0 :   const int index = history_length;
     865                 :            :   char *s;
     866                 :            :   input_method IM;
     867                 :            : 
     868                 :          0 :   IM.prompt      = prompt;
     869                 :          0 :   IM.prompt_cont = prompt_cont;
     870                 :          0 :   IM.getline = &gprl_input;
     871                 :          0 :   IM.free = 1;
     872         [ #  # ]:          0 :   if (! input_loop(F,&IM)) { pari_puts("\n"); return 0; }
     873                 :            : 
     874                 :          0 :   s = F->buf->buf;
     875         [ #  # ]:          0 :   if (*s)
     876                 :            :   {
     877         [ #  # ]:          0 :     if (history_length > index+1)
     878                 :            :     { /* Multi-line input. Remove incomplete lines */
     879                 :          0 :       int i = history_length;
     880         [ #  # ]:          0 :       while (i > index) {
     881                 :          0 :         HIST_ENTRY *e = remove_history(--i);
     882                 :          0 :         pari_free(e->line); pari_free(e);
     883                 :            :       }
     884                 :          0 :       gp_add_history(s);
     885                 :            :     }
     886                 :          0 :     echo_and_log(prompt, s);
     887                 :            :   }
     888                 :          0 :   return 1;
     889                 :            : }
     890                 :            : #endif

Generated by: LCOV version 1.9