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-bordeaux.fr machine (x86_64 architecture), and agregate them in the final report:

The target is to exceed 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) Coverage Total Hit
Test: PARI/GP v2.18.1 lcov report (development 31041-bd73e9fcdd) Lines: 18.3 % 213 39
Test Date: 2026-07-22 22:45:42 Functions: 11.1 % 18 2
Legend: Lines:     hit not hit

            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; either version 2 of the License, or (at your option) any later
       8              : version. It is distributed in the hope that it will be useful, but WITHOUT
       9              : ANY WARRANTY WHATSOEVER.
      10              : 
      11              : Check the License for details. You should have received a copy of it, along
      12              : with the package; see the file 'COPYING'. If not, write to the Free Software
      13              : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14              : 
      15              : /*******************************************************************/
      16              : /*                                                                 */
      17              : /*                 INTERFACE TO READLINE COMPLETION                */
      18              : /*                                                                 */
      19              : /*******************************************************************/
      20              : #include "pari.h"
      21              : 
      22              : #ifdef READLINE
      23              : 
      24              : #include "paripriv.h"
      25              : #include "gp.h"
      26              : 
      27              : typedef int (*RLCI)(int, int); /* rl_complete and rl_insert functions */
      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 pari_rl_interface pari_rl;
      39              : static int did_init_matched = 0;
      40              : 
      41              : static int
      42            0 : change_state(const char *msg, ulong flag, int count)
      43              : {
      44            0 :   int c = (GP_DATA->readline_state & flag) != 0;
      45            0 :   ulong state = GP_DATA->readline_state;
      46              : 
      47            0 :   switch(count)
      48              :   {
      49            0 :     default: c = 0; break; /* off */
      50            0 :     case -1: c = 1; break; /* on  */
      51            0 :     case -2: c = 1 - c; /* toggle */
      52              :   }
      53            0 :   if (c)
      54            0 :     GP_DATA->readline_state |= flag;
      55              :   else {
      56            0 :     GP_DATA->readline_state &= ~flag;
      57            0 :     if (!GP_DATA->readline_state && state) GP_DATA->readline_state = 1;
      58              :   }
      59            0 :   rl_save_prompt();
      60            0 :   rl_message("[%s: %s] ", msg, c? "on": "off");
      61            0 :   c = rl_read_key();
      62            0 :   rl_restore_prompt();
      63            0 :   rl_clear_message();
      64            0 :   rl_stuff_char(c); return 1;
      65              : }
      66              : 
      67              : /* Wrapper around rl_complete to allow toggling insertion of arguments */
      68              : static int
      69            0 : pari_rl_complete(int count, int key)
      70              : {
      71              :   int ret;
      72              : 
      73            0 :   pari_rl.back = 0;
      74            0 :   if (count <= 0)
      75            0 :     return change_state("complete args", DO_ARGS_COMPLETE, count);
      76              : 
      77            0 :   rl_begin_undo_group();
      78            0 :   if (rl_last_func == pari_rl_complete)
      79            0 :     rl_last_func = (RLCI) rl_complete; /* Make repeated TABs different */
      80            0 :   ret = ((RLCI)rl_complete)(count,key);
      81            0 :   if (pari_rl.back && (pari_rl.back <= rl_point))
      82            0 :     rl_point -= pari_rl.back;
      83            0 :   rl_end_undo_group(); return ret;
      84              : }
      85              : 
      86              : static int did_matched_insert;
      87              : 
      88              : static int
      89            0 : pari_rl_matched_insert_suspend(int count, int key)
      90              : {
      91            0 :   ulong state = GP_DATA->readline_state;
      92              :   (void)count; (void)key;
      93              : 
      94            0 :   did_matched_insert = (GP_DATA->readline_state & DO_MATCHED_INSERT);
      95            0 :   GP_DATA->readline_state &= ~DO_MATCHED_INSERT;
      96            0 :   if (!GP_DATA->readline_state && state) GP_DATA->readline_state = 1;
      97            0 :   return 1;
      98              : }
      99              : 
     100              : static int
     101            0 : pari_rl_matched_insert_restore(int count, int key)
     102              : {
     103              :   (void)count; (void)key;
     104            0 :   if (did_matched_insert)
     105            0 :     GP_DATA->readline_state |= DO_MATCHED_INSERT;
     106            0 :   return 1;
     107              : }
     108              : 
     109              : static const char paropen[] = "([{";
     110              : static const char parclose[] = ")]}";
     111              : 
     112              : /* To allow insertion of () with a point in between. */
     113              : static int
     114            0 : pari_rl_matched_insert(int count, int key)
     115              : {
     116            0 :   int i = 0, ret;
     117              : 
     118            0 :   if (count <= 0)
     119            0 :     return change_state("electric parens", DO_MATCHED_INSERT, count);
     120            0 :   while (paropen[i] && paropen[i] != key) i++;
     121            0 :   if (!paropen[i] || !(GP_DATA->readline_state & DO_MATCHED_INSERT) || GP_DATA->flags & gpd_EMACS)
     122            0 :     return ((RLCI)rl_insert)(count,key);
     123            0 :   rl_begin_undo_group();
     124            0 :   ((RLCI)rl_insert)(count,key);
     125            0 :   ret = ((RLCI)rl_insert)(count,parclose[i]);
     126            0 :   rl_point -= count;
     127            0 :   rl_end_undo_group(); return ret;
     128              : }
     129              : 
     130              : static int
     131            0 : pari_rl_default_matched_insert(int count, int key)
     132              : {
     133            0 :     if (!did_init_matched) {
     134            0 :       did_init_matched = 1;
     135            0 :       GP_DATA->readline_state |= DO_MATCHED_INSERT;
     136              :     }
     137            0 :     return pari_rl_matched_insert(count, key);
     138              : }
     139              : 
     140              : static int
     141            0 : pari_rl_forward_sexp(int count, int key)
     142              : {
     143            0 :   int deep = 0, dir = 1, move_point = 0, lfail;
     144              : 
     145              :   (void)key;
     146            0 :   if (count < 0)
     147              :   {
     148            0 :     count = -count; dir = -1;
     149            0 :     if (!rl_point) goto fail;
     150            0 :     rl_point--;
     151              :   }
     152            0 :   while (count || deep)
     153              :   {
     154            0 :     move_point = 1;        /* Need to move point if moving left. */
     155            0 :     lfail = 0;                /* Do not need to fail left movement yet. */
     156            0 :     while ( !is_keyword_char(rl_line_buffer[rl_point])
     157            0 :             && !strchr("\"([{}])",rl_line_buffer[rl_point])
     158            0 :             && !( (dir == 1)
     159              :                   ? (rl_point >= rl_end)
     160            0 :                   : (rl_point <= 0 && (lfail = 1))))
     161            0 :         rl_point += dir;
     162            0 :     if (lfail || !rl_line_buffer[rl_point]) goto fail;
     163              : 
     164            0 :     if (is_keyword_char(rl_line_buffer[rl_point]))
     165              :     {
     166            0 :       while ( is_keyword_char(rl_line_buffer[rl_point])
     167            0 :               && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0 && (lfail = 1)))
     168            0 :                   || (move_point = 0)))
     169            0 :         rl_point += dir;
     170            0 :       if (deep && lfail) goto fail;
     171            0 :       if (!deep) count--;
     172              :     }
     173            0 :     else if (strchr(paropen,rl_line_buffer[rl_point]))
     174              :     {
     175            0 :       if (deep == 0 && dir == -1) goto fail; /* We are already out of pars. */
     176            0 :       rl_point += dir;
     177            0 :       deep++; if (!deep) count--;
     178              :     }
     179            0 :     else if (strchr(parclose,rl_line_buffer[rl_point]))
     180              :     {
     181            0 :       if (deep == 0 && dir == 1)
     182              :       {
     183            0 :         rl_point++; goto fail; /* Get out of pars. */
     184              :       }
     185            0 :       rl_point += dir;
     186            0 :       deep--; if (!deep) count--;
     187              :     }
     188            0 :     else if (rl_line_buffer[rl_point] == '\"')
     189              :     {
     190            0 :       int bad = 1;
     191              : 
     192            0 :       rl_point += dir;
     193            0 :       while ( ((rl_line_buffer[rl_point] != '\"') || (bad = 0))
     194            0 :               && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0))
     195            0 :                   || (move_point = 0)) )
     196            0 :         rl_point += dir;
     197            0 :       if (bad) goto fail;
     198            0 :       rl_point += dir;        /* Skip the other delimiter */
     199            0 :       if (!deep) count--;
     200              :     }
     201              :     else
     202              :     {
     203            0 :       fail: rl_ding(); return 1;
     204              :     }
     205              :   }
     206            0 :   if (dir != 1 && move_point) rl_point++;
     207            0 :   return 1;
     208              : }
     209              : 
     210              : static int
     211            0 : pari_rl_backward_sexp(int count, int key)
     212              : {
     213            0 :   return pari_rl_forward_sexp(-count, key);
     214              : }
     215              : 
     216              : static void
     217            0 : rl_print_aide(char *s, int flag)
     218              : {
     219            0 :   int p = rl_point, e = rl_end;
     220            0 :   FILE *save = pari_outfile;
     221              : 
     222            0 :   rl_point = 0; rl_end = 0; pari_outfile = rl_outstream;
     223            0 :   rl_save_prompt();
     224            0 :   rl_message("%s",""); /* rl_message("") ==> "zero length format" warning */
     225            0 :   gp_help(s, flag);
     226            0 :   rl_restore_prompt();
     227            0 :   rl_point = p; rl_end = e; pari_outfile = save;
     228            0 :   rl_clear_message();
     229            0 :   rl_refresh_line(0,0);
     230            0 : }
     231              : 
     232              : /* long help if count < 0 */
     233              : static int
     234            0 : rl_short_help(int count, int key)
     235              : {
     236            0 :   int flag = h_RL;
     237            0 :   char *s = rl_line_buffer + rl_point;
     238              : 
     239              :   (void)key;
     240              :   /* func() with cursor on ')', e.g. following completion */
     241            0 :   if (s > rl_line_buffer && *s == ')' && s[-1] == '(') s--;
     242            0 :   while (s > rl_line_buffer && is_keyword_char(s[-1])) s--;
     243              :   /* check for '\c' */
     244            0 :   if (s > rl_line_buffer && s[-1] == '\\') s--;
     245              : 
     246            0 :   if (count < 0 || rl_last_func == rl_short_help) flag |= h_LONG;
     247            0 :   rl_print_aide(s, flag); return 0;
     248              : }
     249              : 
     250              : static int
     251            0 : rl_long_help(int count, int key) { (void)count; return rl_short_help(-1,key); }
     252              : 
     253              : static void
     254         1887 : init_histfile(void)
     255              : {
     256         1887 :   char *h = GP_DATA->histfile;
     257         1887 :   if (h && read_history(h)) write_history(h);
     258         1887 : }
     259              : 
     260              : /*******************************************************************/
     261              : /*                                                                 */
     262              : /*                   GET LINE FROM READLINE                        */
     263              : /*                                                                 */
     264              : /*******************************************************************/
     265              : static int
     266            0 : history_is_new(char *s)
     267              : {
     268              :   HIST_ENTRY *e;
     269            0 :   if (!*s) return 0;
     270            0 :   if (!history_length) return 1;
     271            0 :   e = history_get(history_base+history_length-1);
     272              :   /* paranoia: e != NULL, unless readline is in a weird state */
     273            0 :   return e? strcmp(s, e->line): 0;
     274              : }
     275              : 
     276              : static void
     277            0 : gp_add_history(char *s)
     278              : {
     279            0 :   if (history_is_new(s)) { add_history(s); append_history(1,GP_DATA->histfile); }
     280            0 : }
     281              : 
     282              : /* Read line; returns a malloc()ed string of the user input or NULL on EOF.
     283              :    Increments the buffer size appropriately if needed; fix *endp if so. */
     284              : static char *
     285            0 : gprl_input(char **endp, int first, input_method *IM, filtre_t *F)
     286              : {
     287            0 :   pari_sp av = avma;
     288            0 :   Buffer *b = F->buf;
     289            0 :   ulong used = *endp - b->buf;
     290            0 :   ulong left = b->len - used, l;
     291              :   const char *p;
     292              :   char *s, *t;
     293              : 
     294            0 :   if (first) p = IM->prompt;
     295              :   else {
     296            0 :     p = F->in_comment ? GP_DATA->prompt_comment: IM->prompt_cont;
     297            0 :     p = gp_format_prompt(p);
     298              :   }
     299            0 :   if (! (s = readline(p)) ) { set_avma(av); return NULL; } /* EOF */
     300            0 :   gp_add_history(s); /* Makes a copy */
     301            0 :   l = strlen(s) + 1;
     302              :   /* put back \n that readline stripped. This is needed for
     303              :    * { print("a
     304              :    *   b"); }
     305              :    * and conforms with the other input methods anyway. */
     306            0 :   t = (char*)pari_malloc(l + 1);
     307            0 :   memcpy(t, s, l-1);
     308            0 :   pari_free(s); /* readline use malloc */
     309            0 :   t[l-1] = '\n';
     310            0 :   t[l]   = 0; /* equivalent to sprintf(t,"%s\n", s) */
     311            0 :   if (left < l)
     312              :   {
     313            0 :     ulong incr = b->len;
     314            0 :     if (incr < l) incr = l;
     315            0 :     fix_buffer(b, b->len + incr);
     316            0 :     *endp = b->buf + used;
     317              :   }
     318            0 :   set_avma(av); return t;
     319              : }
     320              : 
     321              : /* request one line interactively.
     322              :  * Return 0: EOF
     323              :  *        1: got one line from readline or pari_infile */
     324              : int
     325            0 : get_line_from_readline(const char *prompt, const char *prompt_cont, filtre_t *F)
     326              : {
     327            0 :   const int index = history_length;
     328              :   char *s;
     329              :   input_method IM;
     330              : 
     331            0 :   if (!GP_DATA->use_readline)
     332              :   {
     333            0 :     pari_puts(prompt); pari_flush();
     334            0 :     return get_line_from_file(prompt, F, pari_infile);
     335              :   }
     336              : 
     337            0 :   IM.prompt      = prompt;
     338            0 :   IM.prompt_cont = prompt_cont;
     339            0 :   IM.getline = &gprl_input;
     340            0 :   IM.free = 1;
     341            0 :   if (! input_loop(F,&IM)) { pari_puts("\n"); return 0; }
     342              : 
     343            0 :   s = F->buf->buf;
     344            0 :   if (*s)
     345              :   {
     346            0 :     if (history_length > index+1)
     347              :     { /* Multi-line input. Remove incomplete lines */
     348            0 :       int i = history_length;
     349            0 :       while (i > index) {
     350            0 :         HIST_ENTRY *e = remove_history(--i);
     351            0 :         pari_free(e->line); pari_free(e);
     352              :       }
     353            0 :       gp_add_history(s);
     354              :     }
     355            0 :     gp_echo_and_log(prompt, s);
     356              :   }
     357            0 :   return 1;
     358              : }
     359              : 
     360              : static char**
     361            0 : gp_completion(char *text, int START, int END)
     362              : {
     363            0 :   return pari_completion(&pari_rl, text, START, END);
     364              : }
     365              : 
     366              : void
     367         1887 : init_readline(void)
     368              : {
     369              :   static int init_done = 0;
     370              : 
     371         1887 :   if (init_done) return;
     372              : 
     373         1887 :   pari_use_readline(pari_rl);
     374              : 
     375         1887 :   if (! GP_DATA->use_readline) GP_DATA->readline_state = 0;
     376         1887 :   init_done = 1;
     377         1887 :   init_histfile();
     378         1887 :   cb_pari_init_histfile = init_histfile;
     379         1887 :   cb_pari_get_line_interactive = get_line_from_readline;
     380              : 
     381              :   /* Allow conditional parsing of the ~/.inputrc file. */
     382         1887 :   rl_readline_name = "Pari-GP";
     383              : 
     384              :   /* added [~?] */
     385         1887 :   rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(?~";
     386         1887 :   rl_special_prefixes = "~";
     387              : 
     388              :   /* custom completer */
     389         1887 :   rl_attempted_completion_function = (rl_completion_func_t*) gp_completion;
     390              : 
     391              :   /* we always want the whole list of completions under emacs */
     392         1887 :   if (GP_DATA->flags & gpd_EMACS) rl_completion_query_items = 0x8fff;
     393              : 
     394         1887 :   rl_add_defun("short-help", rl_short_help, -1);
     395         1887 :   rl_add_defun("long-help", rl_long_help, -1);
     396         1887 :   rl_add_defun("pari-complete", pari_rl_complete, '\t');
     397         1887 :   rl_add_defun("pari-matched-insert", pari_rl_default_matched_insert, -1);
     398         1887 :   rl_add_defun("pari-matched-insert-suspend", pari_rl_matched_insert_suspend, -1);
     399         1887 :   rl_add_defun("pari-matched-insert-restore", pari_rl_matched_insert_restore, -1);
     400         1887 :   rl_add_defun("pari-forward-sexp", pari_rl_forward_sexp, -1);
     401         1887 :   rl_add_defun("pari-backward-sexp", pari_rl_backward_sexp, -1);
     402              : 
     403         1887 :   rl_bind_key_in_map('h', rl_short_help, emacs_meta_keymap);
     404         1887 :   rl_bind_key_in_map('H', rl_long_help,  emacs_meta_keymap);
     405              : 
     406              : #define KSbind(s,f,k) rl_generic_bind(ISFUNC, (s), (char*)(f), (k))
     407              : 
     408         1887 :   KSbind("OP",   rl_short_help,  emacs_meta_keymap); /* f1, vt100 */
     409         1887 :   KSbind("[11~", rl_short_help,  emacs_meta_keymap); /* f1, xterm */
     410         1887 :   KSbind("OP",   rl_short_help,  vi_movement_keymap); /* f1, vt100 */
     411         1887 :   KSbind("[11~", rl_short_help,  vi_movement_keymap); /* f1, xterm */
     412              :   /* XTerm may signal start/end of paste by emitting F200/F201
     413              :    * TODO: check to what extent this patch has been applied */
     414              :   /* FIXME: For vi mode something more intelligent is needed - to switch to the
     415              :      insert mode - and back when restoring. */
     416         1887 :   KSbind("[200~", pari_rl_matched_insert_suspend,  emacs_meta_keymap);  /* pre-paste xterm */
     417         1887 :   KSbind("[200~", pari_rl_matched_insert_suspend,  vi_movement_keymap); /* pre-paste xterm */
     418         1887 :   KSbind("[201~", pari_rl_matched_insert_restore,  emacs_meta_keymap);  /* post-paste xterm */
     419         1887 :   KSbind("[201~", pari_rl_matched_insert_restore,  vi_movement_keymap); /* post-paste xterm */
     420         1887 :   rl_bind_key_in_map('(', pari_rl_matched_insert, emacs_standard_keymap);
     421         1887 :   rl_bind_key_in_map('[', pari_rl_matched_insert, emacs_standard_keymap);
     422         1887 :   rl_bind_key_in_map(6, pari_rl_forward_sexp,  emacs_meta_keymap); /* M-C-f */
     423         1887 :   rl_bind_key_in_map(2, pari_rl_backward_sexp, emacs_meta_keymap); /* M-C-b */
     424              : }
     425              : #endif
        

Generated by: LCOV version 2.0-1