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) Hit Total Coverage
Test: PARI/GP v2.18.0 lcov report (development 29796-c9bd12ad19) Lines: 39 213 18.3 %
Date: 2024-12-09 09:09:46 Functions: 2 18 11.1 %
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        1862 : init_histfile(void)
     255             : {
     256        1862 :   char *h = GP_DATA->histfile;
     257        1862 :   if (h && read_history(h)) write_history(h);
     258        1862 : }
     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        1862 : init_readline(void)
     368             : {
     369             :   static int init_done = 0;
     370             : 
     371        1862 :   if (init_done) return;
     372             : 
     373        1862 :   pari_use_readline(pari_rl);
     374             : 
     375        1862 :   if (! GP_DATA->use_readline) GP_DATA->readline_state = 0;
     376        1862 :   init_done = 1;
     377        1862 :   init_histfile();
     378        1862 :   cb_pari_init_histfile = init_histfile;
     379        1862 :   cb_pari_get_line_interactive = get_line_from_readline;
     380             : 
     381             :   /* Allow conditional parsing of the ~/.inputrc file. */
     382        1862 :   rl_readline_name = "Pari-GP";
     383             : 
     384             :   /* added ~, ? and , */
     385        1862 :   rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(?~";
     386        1862 :   rl_special_prefixes = "~";
     387             : 
     388             :   /* custom completer */
     389        1862 :   rl_attempted_completion_function = (rl_completion_func_t*) gp_completion;
     390             : 
     391             :   /* we always want the whole list of completions under emacs */
     392        1862 :   if (GP_DATA->flags & gpd_EMACS) rl_completion_query_items = 0x8fff;
     393             : 
     394        1862 :   rl_add_defun("short-help", rl_short_help, -1);
     395        1862 :   rl_add_defun("long-help", rl_long_help, -1);
     396        1862 :   rl_add_defun("pari-complete", pari_rl_complete, '\t');
     397        1862 :   rl_add_defun("pari-matched-insert", pari_rl_default_matched_insert, -1);
     398        1862 :   rl_add_defun("pari-matched-insert-suspend", pari_rl_matched_insert_suspend, -1);
     399        1862 :   rl_add_defun("pari-matched-insert-restore", pari_rl_matched_insert_restore, -1);
     400        1862 :   rl_add_defun("pari-forward-sexp", pari_rl_forward_sexp, -1);
     401        1862 :   rl_add_defun("pari-backward-sexp", pari_rl_backward_sexp, -1);
     402             : 
     403        1862 :   rl_bind_key_in_map('h', rl_short_help, emacs_meta_keymap);
     404        1862 :   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        1862 :   KSbind("OP",   rl_short_help,  emacs_meta_keymap); /* f1, vt100 */
     409        1862 :   KSbind("[11~", rl_short_help,  emacs_meta_keymap); /* f1, xterm */
     410        1862 :   KSbind("OP",   rl_short_help,  vi_movement_keymap); /* f1, vt100 */
     411        1862 :   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        1862 :   KSbind("[200~", pari_rl_matched_insert_suspend,  emacs_meta_keymap);  /* pre-paste xterm */
     417        1862 :   KSbind("[200~", pari_rl_matched_insert_suspend,  vi_movement_keymap); /* pre-paste xterm */
     418        1862 :   KSbind("[201~", pari_rl_matched_insert_restore,  emacs_meta_keymap);  /* post-paste xterm */
     419        1862 :   KSbind("[201~", pari_rl_matched_insert_restore,  vi_movement_keymap); /* post-paste xterm */
     420        1862 :   rl_bind_key_in_map('(', pari_rl_matched_insert, emacs_standard_keymap);
     421        1862 :   rl_bind_key_in_map('[', pari_rl_matched_insert, emacs_standard_keymap);
     422        1862 :   rl_bind_key_in_map(6, pari_rl_forward_sexp,  emacs_meta_keymap); /* M-C-f */
     423        1862 :   rl_bind_key_in_map(2, pari_rl_backward_sexp, emacs_meta_keymap); /* M-C-b */
     424             : }
     425             : #endif

Generated by: LCOV version 1.16