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 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 - language - readline.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.10.0 lcov report (development 21348-d75f58f) Lines: 0 191 0.0 %
Date: 2017-11-20 06:21:05 Functions: 0 17 0.0 %
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. 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             : #include "paripriv.h"
      21             : 
      22             : /**************************************************************************/
      23             : static entree *current_ep = NULL;
      24             : 
      25             : /* do we add () at the end of completed word? (is it a function?) */
      26             : static int
      27           0 : add_paren(pari_rl_interface *rl, int end)
      28             : {
      29             :   entree *ep;
      30             :   const char *s;
      31             : 
      32           0 :   if (end < 0 || (*rl->line_buffer)[end] == '(')
      33           0 :     return 0; /* not from command_generator or already there */
      34           0 :   ep = do_alias(current_ep); /* current_ep set in command_generator */
      35           0 :   if (EpVALENCE(ep) < EpNEW)
      36             :   { /* is it a constant masked as a function (e.g Pi)? */
      37           0 :     s = ep->help; if (!s) return 1;
      38           0 :     while (is_keyword_char(*s)) s++;
      39           0 :     return (*s != '=');
      40             :   }
      41           0 :   switch(EpVALENCE(ep))
      42             :   {
      43           0 :     case EpVAR: return typ((GEN)ep->value) == t_CLOSURE;
      44           0 :     case EpINSTALL: return 1;
      45             :   }
      46           0 :   return 0;
      47             : }
      48             : 
      49             : static void
      50           0 : match_concat(char **matches, const char *s)
      51             : {
      52           0 :   matches[0] = (char*)pari_realloc((void*)matches[0],
      53           0 :                                 strlen(matches[0])+strlen(s)+1);
      54           0 :   strcat(matches[0],s);
      55           0 : }
      56             : 
      57             : #define add_comma(x) (x==-2) /* from default_generator */
      58             : 
      59             : /* a single match, possibly modify matches[0] in place */
      60             : static void
      61           0 : treat_single(pari_rl_interface *rl, int code, char **matches)
      62             : {
      63           0 :   if (add_paren(rl, code))
      64             :   {
      65           0 :     match_concat(matches,"()");
      66           0 :     rl->back = 1;
      67           0 :     if (*rl->point == *rl->end)
      68           0 :     *rl->completion_append_character = '\0'; /* Do not append space. */
      69             :   }
      70           0 :   else if (add_comma(code))
      71           0 :     match_concat(matches,",");
      72           0 : }
      73             : #undef add_comma
      74             : 
      75             : 
      76             : static char **
      77           0 : matches_for_emacs(const char *text, char **matches)
      78             : {
      79           0 :   if (!matches) printf("@");
      80             :   else
      81             :   {
      82             :     int i;
      83           0 :     printf("%s@", matches[0] + strlen(text));
      84           0 :     if (matches[1]) print_fun_list(matches+1,0);
      85             : 
      86             :    /* we don't want readline to do anything, but insert some junk
      87             :     * which will be erased by emacs.
      88             :     */
      89           0 :     for (i=0; matches[i]; i++) pari_free(matches[i]);
      90           0 :     pari_free(matches);
      91             :   }
      92           0 :   matches = (char **) pari_malloc(2*sizeof(char *));
      93           0 :   matches[0] = (char*)pari_malloc(2); sprintf(matches[0],"_");
      94           0 :   matches[1] = NULL; printf("@E_N_D"); pari_flush();
      95           0 :   return matches;
      96             : }
      97             : 
      98             : /* Attempt to complete on the contents of TEXT. 'code' is used to
      99             :  * differentiate between callers when a single match is found.
     100             :  * Return the array of matches, NULL if there are none. */
     101             : static char **
     102           0 : get_matches(pari_rl_interface *rl, int code, const char *text, char *(*f)(const char*, int))
     103             : {
     104           0 :   char **matches = rl->completion_matches(text, f);
     105           0 :   if (matches && !matches[1]) treat_single(rl, code, matches);
     106           0 :   if (GP_DATA->flags & gpd_EMACS) matches = matches_for_emacs(text,matches);
     107           0 :   return matches;
     108             : }
     109             : 
     110             : static char *
     111           0 : add_prefix(const char *name, const char *text, long junk)
     112             : {
     113           0 :   char *s = strncpy((char*)pari_malloc(strlen(name)+1+junk),text,junk);
     114           0 :   strcpy(s+junk,name); return s;
     115             : }
     116             : static void
     117           0 : init_prefix(const char *text, int *len, int *junk, char **TEXT)
     118             : {
     119           0 :   long l = strlen(text), j = l-1;
     120           0 :   while (j >= 0 && is_keyword_char(text[j])) j--;
     121           0 :   if (text[j] == '-' && j >= 7 && !strncmp(text+(j-7),"refcard",7)) j -= 8;
     122           0 :   j++;
     123           0 :   *TEXT = (char*)text + j;
     124           0 :   *junk = j;
     125           0 :   *len  = l - j;
     126           0 : }
     127             : 
     128             : static int
     129           0 : is_internal(entree *ep) { return *ep->name == '_'; }
     130             : 
     131             : /* Generator function for command completion.  STATE lets us know whether
     132             :  * to start from scratch; without any state (i.e. STATE == 0), then we
     133             :  * start at the top of the list. */
     134             : static char *
     135           0 : hashtable_generator(const char *text, int state, entree **hash)
     136             : {
     137             :   static int hashpos, len, junk;
     138             :   static entree* ep;
     139             :   static char *TEXT;
     140             : 
     141             :  /* If this is a new word to complete, initialize now:
     142             :   *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
     143             :   *  + file completion and keyword completion use different word boundaries,
     144             :   *    have TEXT point to the keyword start.
     145             :   *  + save the length of TEXT for efficiency.
     146             :   */
     147           0 :   if (!state)
     148             :   {
     149           0 :     hashpos = 0; ep = hash[hashpos];
     150           0 :     init_prefix(text, &len, &junk, &TEXT);
     151             :   }
     152             : 
     153             :   /* Return the next name which partially matches from the command list. */
     154             :   for(;;)
     155           0 :     if (!ep)
     156             :     {
     157           0 :       if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
     158           0 :       ep = hash[hashpos];
     159             :     }
     160           0 :     else if (is_internal(ep) || strncmp(ep->name,TEXT,len))
     161           0 :       ep = ep->next;
     162             :     else
     163           0 :       break;
     164           0 :   current_ep = ep; ep = ep->next;
     165           0 :   return add_prefix(current_ep->name,text,junk);
     166             : }
     167             : /* Generator function for member completion.  STATE lets us know whether
     168             :  * to start from scratch; without any state (i.e. STATE == 0), then we
     169             :  * start at the top of the list. */
     170             : static char *
     171           0 : member_generator(const char *text, int state)
     172             : {
     173             :   static int hashpos, len, junk;
     174             :   static entree* ep;
     175             :   static char *TEXT;
     176           0 :   entree **hash=functions_hash;
     177             : 
     178             :  /* If this is a new word to complete, initialize now:
     179             :   *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
     180             :   *  + file completion and keyword completion use different word boundaries,
     181             :   *    have TEXT point to the keyword start.
     182             :   *  + save the length of TEXT for efficiency.
     183             :   */
     184           0 :   if (!state)
     185             :   {
     186           0 :     hashpos = 0; ep = hash[hashpos];
     187           0 :     init_prefix(text, &len, &junk, &TEXT);
     188             :   }
     189             : 
     190             :   /* Return the next name which partially matches from the command list. */
     191             :   for(;;)
     192           0 :     if (!ep)
     193             :     {
     194           0 :       if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
     195           0 :       ep = hash[hashpos];
     196             :     }
     197           0 :     else if (ep->name[0]=='_' && ep->name[1]=='.'
     198           0 :              && !strncmp(ep->name+2,TEXT,len))
     199             :         break;
     200             :     else
     201           0 :         ep = ep->next;
     202           0 :   current_ep = ep; ep = ep->next;
     203           0 :   return add_prefix(current_ep->name+2,text,junk);
     204             : }
     205             : static char *
     206           0 : command_generator(const char *text, int state)
     207           0 : { return hashtable_generator(text,state, functions_hash); }
     208             : static char *
     209           0 : default_generator(const char *text,int state)
     210           0 : { return hashtable_generator(text,state, defaults_hash); }
     211             : 
     212             : static char *
     213           0 : ext_help_generator(const char *text, int state)
     214             : {
     215             :   static int len, junk, n, def, key;
     216             :   static char *TEXT;
     217           0 :   if (!state) {
     218           0 :     n = 0;
     219           0 :     def = key = 1;
     220           0 :     init_prefix(text, &len, &junk, &TEXT);
     221             :   }
     222           0 :   if (def)
     223             :   {
     224           0 :     char *s = default_generator(TEXT, state);
     225           0 :     if (s) return add_prefix(s, text, junk);
     226           0 :     def = 0;
     227             :   }
     228           0 :   if (key)
     229             :   {
     230           0 :     const char **L = gphelp_keyword_list();
     231           0 :     for ( ; L[n]; n++)
     232           0 :       if (!strncmp(L[n],TEXT,len))
     233           0 :         return add_prefix(L[n++], text, junk);
     234           0 :     key = 0; state = 0;
     235             :   }
     236           0 :   return command_generator(text, state);
     237             : }
     238             : 
     239             : /* add a space between \<char> and following text. Attempting completion now
     240             :  * would delete char. Hitting <TAB> again will complete properly */
     241             : static char **
     242           0 : add_space(pari_rl_interface *rl, int start)
     243             : {
     244             :   char **m;
     245           0 :   int p = *rl->point + 1;
     246           0 :   *rl->point = start + 2;
     247           0 :   rl->insert(1, ' '); *rl->point = p;
     248             :   /*better: fake an empty completion, but don't append ' ' after it! */
     249           0 :   *rl->completion_append_character = '\0';
     250           0 :   m = (char**)pari_malloc(2 * sizeof(char*));
     251           0 :   m[0] = (char*)pari_malloc(1); *(m[0]) = 0;
     252           0 :   m[1] = NULL; return m;
     253             : }
     254             : 
     255             : char **
     256           0 : pari_completion(pari_rl_interface *rl, char *text, int START, int END)
     257             : {
     258           0 :   int i, first=0, start=START;
     259           0 :   char *line = *rl->line_buffer;
     260             : 
     261           0 :   *rl->completion_append_character = ' ';
     262           0 :   current_ep = NULL;
     263           0 :   while (line[first] && isspace((int)line[first])) first++;
     264           0 :   if (line[first] == '?')
     265             :   {
     266           0 :       if (line[first+1] == '?')
     267           0 :         return get_matches(rl, -1, text, ext_help_generator);
     268           0 :       return get_matches(rl, -1, text, command_generator);
     269             :   }
     270             : 
     271             : /* If the line does not begin by a backslash, then it is:
     272             :  * . an old command ( if preceded by "whatnow(" ).
     273             :  * . a default ( if preceded by "default(" ).
     274             :  * . a member function ( if preceded by "." + keyword_chars )
     275             :  * . a file name (in current directory) ( if preceded by 'read' or 'writexx' )
     276             :  * . a command */
     277           0 :   if (start >=1 && line[start] != '~') start--;
     278           0 :   while (start && is_keyword_char(line[start])) start--;
     279           0 :   if (line[start] == '~')
     280             :   {
     281             :     char *(*f)(const char*, int);
     282           0 :     f = rl->username_completion_function;
     283           0 :     for(i=start+1;i<=END;i++)
     284           0 :       if (line[i] == '/') { f = rl->filename_completion_function; break; }
     285           0 :     return get_matches(rl, -1, text, f);
     286             :   }
     287           0 :   if (line[first] == '\\')
     288             :   {
     289           0 :     if (first == start) return add_space(rl, start);
     290           0 :     return get_matches(rl, -1, text, rl->filename_completion_function);
     291             :   }
     292             : 
     293           0 :   while (start && line[start] != '('
     294           0 :                && line[start] != ',') start--;
     295           0 :   if (line[start] == '(' && start)
     296             :   {
     297             :     int iend, j,k;
     298             :     entree *ep;
     299             :     char buf[200];
     300             : 
     301           0 :     i = start;
     302             : 
     303           0 :     while (i && isspace((int)line[i-1])) i--;
     304           0 :     iend = i;
     305           0 :     while (i && is_keyword_char(line[i-1])) i--;
     306             : 
     307           0 :     if (strncmp(line + i,"default",7) == 0)
     308           0 :       return get_matches(rl, -2, text, default_generator);
     309           0 :     if ( strncmp(line + i,"read",4)  == 0
     310           0 :       || strncmp(line + i,"write",5) == 0)
     311           0 :       return get_matches(rl, -1, text, rl->filename_completion_function);
     312             : 
     313           0 :     j = start + 1;
     314           0 :     while (j <= END && isspace((int)line[j])) j++;
     315           0 :     k = END;
     316           0 :     while (k > j && isspace((int)line[k])) k--;
     317             :     /* If we are in empty parens, insert the default arguments */
     318           0 :     if ((GP_DATA->readline_state & DO_ARGS_COMPLETE) && k == j
     319           0 :          && (line[j] == ')' || !line[j])
     320           0 :          && (iend - i < (long)sizeof(buf))
     321           0 :          && ( strncpy(buf, line + i, iend - i),
     322           0 :               buf[iend - i] = 0, 1)
     323           0 :          && (ep = is_entry(buf)) && ep->help)
     324             :     {
     325           0 :       const char *s = ep->help;
     326           0 :       while (is_keyword_char(*s)) s++;
     327           0 :       if (*s++ == '(')
     328             :       { /* function call: insert arguments */
     329           0 :         const char *e = s;
     330           0 :         while (*e && *e != ')' && *e != '(') e++;
     331           0 :         if (*e == ')')
     332             :         { /* we just skipped over the arguments in short help text */
     333           0 :           char *str = strncpy((char*)pari_malloc(e-s + 1), s, e-s);
     334           0 :           char **ret = (char**)pari_malloc(sizeof(char*)*2);
     335           0 :           str[e-s] = 0;
     336           0 :           ret[0] = str; ret[1] = NULL;
     337           0 :           if (GP_DATA->flags & gpd_EMACS) ret = matches_for_emacs("",ret);
     338           0 :           return ret;
     339             :         }
     340             :       }
     341             :     }
     342             :   }
     343           0 :   for(i = END-1; i >= start; i--)
     344           0 :     if (!is_keyword_char(line[i]))
     345             :     {
     346           0 :       if (line[i] == '.')
     347           0 :         return get_matches(rl, -1, text, member_generator);
     348           0 :       break;
     349             :     }
     350           0 :   return get_matches(rl, END, text, command_generator);
     351             : }
     352             : 
     353             : char *
     354           0 : pari_completion_word(pari_rl_interface *rl, long end)
     355             : {
     356           0 :   char *line = *rl->line_buffer;
     357           0 :   char *s = line + end, *found_quote = NULL;
     358             :   long i;
     359             :   /* truncate at cursor position */
     360           0 :   *s = 0;
     361             :   /* first look for unclosed string */
     362           0 :   for (i=0; i < end; i++)
     363             :   {
     364           0 :     switch(line[i])
     365             :     {
     366             :       case '"':
     367           0 :         found_quote = found_quote? NULL: line + i;
     368           0 :         break;
     369             : 
     370           0 :       case '\\': i++; break;
     371             :     }
     372             : 
     373             :   }
     374           0 :   if (found_quote) return found_quote + 1; /* return next char after quote */
     375             : 
     376             :   /* else find beginning of word */
     377           0 :   while (s > line)
     378             :   {
     379           0 :     s--;
     380           0 :     if (!is_keyword_char(*s)) { s++; break; }
     381             :   }
     382           0 :   return s;
     383             : }
     384             : 
     385             : char **
     386           0 : pari_completion_matches(pari_rl_interface *rl, const char *s, long pos, long *wordpos)
     387             : {
     388             :   char *text;
     389             :   char **matches;
     390             :   long w;
     391             : 
     392           0 :   if (*rl->line_buffer) pari_free(*rl->line_buffer);
     393           0 :   *rl->line_buffer = pari_strdup(s);
     394             : 
     395           0 :   text = pari_completion_word(rl, pos);
     396           0 :   w = text - *rl->line_buffer;
     397           0 :   if (wordpos) *wordpos = w;
     398             :   /* text = start of expression we complete */
     399           0 :   *rl->end = strlen(s)-1;
     400           0 :   *rl->point = pos;
     401           0 :   matches = pari_completion(rl, text, w, pos);
     402           0 :   return matches;
     403             : }

Generated by: LCOV version 1.11