1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  file rt_complete.c
4  *  Copyright (C) 2007-2017 The R Core Team.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, a copy is available at
18  *  https://www.R-project.org/Licenses/
19  */
20 
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <getline/getline.h>
27 #include <string.h>
28 #include <stdlib.h> /* for getenv */
29 
30 #ifndef min
31 /* in stdlib.h in Win64 headers */
32 # define min(a, b) (a < b ? a : b)
33 #endif
34 
35 #include <Rinternals.h>
36 #include <R_ext/Parse.h>
37 
38 static int completion_available = -1;
39 
gl_tab(char * buf,int offset,int * loc)40 static int gl_tab(char *buf, int offset, int *loc)
41 /* default tab handler, acts like tabstops every 8 cols */
42 {
43     int i, count, len;
44 
45     len = strlen(buf);
46     count = 8 - (offset + *loc) % 8;
47     for (i=len; i >= *loc; i--)
48 	buf[i+count] = buf[i];
49     for (i=0; i < count; i++)
50 	buf[*loc+i] = ' ';
51     i = *loc;
52     *loc = i + count;
53     return i;
54 }
55 
rt_completion(char * buf,int offset,int * loc)56 static int rt_completion(char *buf, int offset, int *loc)
57 {
58     int i, alen, cursor_position = *loc;
59     char *partial_line = buf;
60     const char *additional_text;
61     SEXP cmdSexp, cmdexpr, ans = R_NilValue;
62     ParseStatus status;
63 
64     if(!completion_available) return gl_tab(buf, offset, loc);
65 
66     if(completion_available < 0) {
67 	char *p = getenv("R_COMPLETION");
68 	if(p && strcmp(p, "FALSE") == 0) {
69 	    completion_available = 0;
70 	    return gl_tab(buf, offset, loc);
71 	}
72 	/* First check if namespace is loaded */
73 	if(findVarInFrame(R_NamespaceRegistry, install("utils"))
74 	   != R_UnboundValue) completion_available = 1;
75 	else { /* Then try to load it */
76 	    char *p = "try(loadNamespace('utils'), silent=TRUE)";
77 	    PROTECT(cmdSexp = mkString(p));
78 	    cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
79 	    if(status == PARSE_OK) {
80 		for(i = 0; i < length(cmdexpr); i++)
81 		    eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
82 	    }
83 	    UNPROTECT(2);
84 	    if(findVarInFrame(R_NamespaceRegistry, install("utils"))
85 	       != R_UnboundValue) completion_available = 1;
86 	    else {
87 		completion_available = 0;
88 		return -1; /* no change */
89 	    }
90 	}
91     }
92 
93     alen = strlen(partial_line);
94     char orig[alen + 1], pline[2*alen + 1],
95             *pchar = pline, achar;
96     strcpy(orig, partial_line);
97     for (i = 0; i < alen; i++) {
98         achar = orig[i];
99 	if (achar == '"' || achar == '\\') *pchar++ = '\\';
100 	*pchar++ = achar;
101     }
102     *pchar = 0;
103     size_t len = strlen(pline) + 100;
104     char cmd[len];
105     snprintf(cmd, len,
106 	     "utils:::.win32consoleCompletion(\"%s\", %d)",
107 	     pline, cursor_position);
108     PROTECT(cmdSexp = mkString(cmd));
109     cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
110     if (status != PARSE_OK) {
111 	UNPROTECT(2);
112 	/* Uncomment next line to debug */
113 	/* Rprintf("failed: %s \n", cmd); */
114 	/* otherwise pretend that nothing happened and return */
115 	return -1; /* no change */
116     }
117     /* Loop is needed here as EXPRSEXP will be of length > 1 */
118     for(i = 0; i < length(cmdexpr); i++)
119 	ans = eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
120     UNPROTECT(2);
121 
122     /* ans has the form list(addition, possible), where 'addition' is
123        unique additional text if any, and 'possible' is a character
124        vector holding possible completions if any (already formatted
125        for linewise printing in the current implementation).  If
126        'possible' has any content, we want to print those (or show in
127        status bar or whatever).  Otherwise add the 'additional' text
128        at the cursor */
129 
130 #define ADDITION 0
131 #define POSSIBLE 1
132 
133     alen = length(VECTOR_ELT(ans, POSSIBLE));
134     if (alen) {
135 	int max_show = 10;
136 	printf("\n"); /* finish current line */
137 	for (i = 0; i < min(alen, max_show); i++) {
138 	    printf("%s\n", CHAR(STRING_ELT(VECTOR_ELT(ans, POSSIBLE), i)));
139 	}
140 	if (alen > max_show)
141 	    printf("\n[...truncated]\n");
142 	cursor_position = -2; /* Need to redisplay whole line */
143     }
144     additional_text = CHAR(STRING_ELT( VECTOR_ELT(ans, ADDITION), 0 ));
145     alen = strlen(additional_text);
146     if (alen) {
147 	int cp = *loc;
148 	memcpy(buf+cp, additional_text, alen+1);
149 	*loc = cp + alen;
150     }
151     return cursor_position;
152 }
153 
154 
R_gl_tab_set(void)155 void R_gl_tab_set(void)
156 {
157     gl_tab_hook = rt_completion;
158 }
159