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 16937-4bd9b4e) Lines: 38 391 9.7 %
Date: 2014-10-24 Functions: 2 34 5.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 4 355 1.1 %

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

Generated by: LCOV version 1.9