1 /*===========================================================================
2  Copyright (c) 1998-2000, The Santa Cruz Operation
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7 
8  *Redistributions of source code must retain the above copyright notice,
9  this list of conditions and the following disclaimer.
10 
11  *Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following disclaimer in the documentation
13  and/or other materials provided with the distribution.
14 
15  *Neither name of The Santa Cruz Operation nor the names of its contributors
16  may be used to endorse or promote products derived from this software
17  without specific prior written permission.
18 
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  DAMAGE.
31  =========================================================================*/
32 
33 /*
34  *	terminal input functions
35  *
36  *	cscope - interactive C symbol cross-reference
37  */
38 
39 #include "global-cscope.h"
40 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
41 #include <ncurses.h>
42 #else
43 #include <curses.h>
44 #endif
45 #include <setjmp.h>	/* jmp_buf */
46 #include <stdlib.h>
47 #include <errno.h>
48 #if HAVE_SYS_TERMIOS_H
49 #include <sys/termios.h>
50 #endif
51 #if defined(_WIN32) && !defined(__CYGWIN__)
52 #define WIN32_LEAN_AND_MEAN
53 #define __OBJC__	/* work around BOOL redefinition */
54 #include <windows.h>
55 #endif
56 
57 static	jmp_buf	env;		/**< setjmp/longjmp buffer */
58 static	int	prevchar;	/**< previous, ungotten character */
59 
60 /* Internal prototypes: */
61 static RETSIGTYPE catchint(int sig);
62 
63 /** catch the interrupt signal */
64 
65 /*ARGSUSED*/
66 static RETSIGTYPE
catchint(int sig)67 catchint(int sig)
68 {
69  	(void) sig;		/* 'use' it, to avoid a warning */
70 
71 	signal(SIGINT, catchint);
72 	longjmp(env, 1);
73 }
74 
75 /** unget a character */
76 void
myungetch(int c)77 myungetch(int c)
78 {
79 	prevchar = c;
80 }
81 
82 /** get a character from the terminal */
83 int
mygetch(void)84 mygetch(void)
85 {
86 #if !defined(_WIN32) || !defined(__CYGWIN__)
87     /* longjmp won't work, it's in a different thread */
88     sighandler_t savesig; /* old value of signal */
89 #endif
90     int c;
91 
92     /* change an interrupt signal to a break key character */
93     if (setjmp(env) == 0) {
94 #if defined(_WIN32) && !defined(__CYGWIN__)
95 	/* turn off Ctrl+C handling (PDCurses does this, but it gets turned on
96 	   again by CMD during system) */
97 	SetConsoleMode( GetStdHandle( STD_INPUT_HANDLE ), 0 );
98 #else
99 	savesig = signal(SIGINT, catchint);
100 #endif
101 	refresh();	/* update the display */
102 	mousereinit();	/* curses can change the menu number */
103 	if(prevchar) {
104 	    c = prevchar;
105 	    prevchar = 0;
106 	} else {
107 	    c = -1;
108 	    while (c == -1) {
109 		/* get a character from the terminal */
110 		c = getch();
111 		if ((c == -1) && (errno != EINTR))
112 		    break;
113 	    }
114 	}
115     } else {	/* longjmp to here from signal handler */
116 	c = KEY_BREAK;
117     }
118 #if !defined(_WIN32) || !defined(__CYGWIN__)
119     signal(SIGINT, savesig);
120 #endif
121     return(c);
122 }
123 
124 
125 /** get a line from the terminal in non-canonical mode */
126 int
mygetline(char p[],char s[],unsigned size,int firstchar,BOOL iscaseless)127 mygetline(char p[], char s[], unsigned size, int firstchar, BOOL iscaseless)
128 {
129     int	c;
130     unsigned int i = 0, j;
131     char *sright;	/* substring to the right of the cursor */
132     unsigned int ri = 0;		/* position in right-string */
133 
134     /* Inserts and deletes are always performed on the left-string,
135      * but we'll also have a right-string 'sright' to hold characters
136      * which are on the right of the cursor [insertion point].
137      *
138      * Think of 'sright' as a stack -- we push chars into it when the cursor
139      * moves left, and we pop chars off it when the cursor moves right again.
140      * At the end of the function, we'll pop off any remaining characters
141      * onto the end of 's'
142      */
143     sright = calloc(sizeof(char), size );
144 
145     strcpy ( s, p);
146     i += strlen(p);
147     /* if a character already has been typed */
148     if (firstchar != '\0') {
149 	if(iscaseless == YES) {
150 	    firstchar = tolower(firstchar);
151 	}
152 	addch(firstchar);	/* display it */
153 	s[i++] = firstchar;	/* save it */
154     }
155     /* until the end of the line is reached */
156     while ((c = mygetch()) != '\r' && c != '\n' && c != KEY_ENTER) {
157 	if (c == KEY_LEFT || c == ctrl('B')) {	/* left */
158 	    if (i > 0) {
159 		addch('\b');
160 		/* move this char into the second (rhs) string */
161 		sright[ri++] = s[--i];
162 	    }
163 	} else if (c == KEY_RIGHT || c == ctrl('F')) {	/* right */
164 	    if (i < size && ri > 0) {
165 		/* move this char to the left of the cursor */
166 		s[i++] = sright[--ri];
167 		addch(s[i-1]);
168 	    }
169 	} else if (
170 #ifdef KEY_HOME
171 		   c == KEY_HOME ||
172 #endif
173 		   c == ctrl('A') ) {
174 	    while (i > 0) {
175 		sright[ri++] = s[--i];
176 		addch('\b');
177 		addch(s[i]);
178 		addch('\b');
179 	    }
180 	} else if (
181 #ifdef KEY_END
182 		   c == KEY_END ||
183 #endif
184 		   c == ctrl('E') ) {
185 	    while (ri > 0) {
186 		s[i++] = sright[--ri];
187 		addch(s[i-1]);
188 	    }
189 	} else if (c == erasechar() || c == KEY_BACKSPACE
190 		   || c == DEL || c == ctrl('H') ) {
191 	    /* erase */
192 	    if (i > 0) {
193 		if (ri == 0)  {
194 		    addstr("\b \b");
195 		} else {
196 		    addch('\b');
197 		    delch();
198 		}
199 		s[i] = '\0';
200 		--i;
201 	    }
202 	} else if (c == killchar() || c == KEY_BREAK) {
203 	    /* kill */
204 	    for (j = 0; j < i; ++j) {
205 		addch('\b');
206 	    }
207 	    for (j = 0; j < i; ++j) {
208 		addch(' ');
209 	    }
210 	    for (j = 0; j < i; ++j) {
211 		addch('\b');
212 	    }
213 	    i = 0;
214 	} else if (isprint(c) || c == '\t') {
215 	    /* printable */
216 	    if(iscaseless == YES) {
217 		c = tolower(c);
218 	    }
219 	    /* if it will fit on the line */
220 	    if (i < size) {
221 		s[i++] = c;	/* save it */
222 		if (ri == 0) {
223 		    addch(c);	/* display it */
224 		} else {
225 		    insch(c);	/* display it */
226 		    addch(c);	/* advance cursor */
227 		}
228 	    }
229 #if UNIXPC
230 	} else if (unixpcmouse == YES && c == ESC) {	/* mouse */
231 	    getmouseaction(ESC);	/* ignore it */
232 #endif
233 	} else if (mouse == YES && c == ctrl('X')) {
234 	    getmouseaction(ctrl('X'));	/* ignore it */
235 	} else if (c == EOF) {				/* end-of-file */
236 	    break;
237 	}
238 
239 	/* return on an empty line to allow a command to be entered */
240 	if (firstchar != '\0' && (i+ri) == 0) {
241 	    break;
242 	}
243     }
244 
245     /* move any remaining chars on the rhs of the cursor
246      * onto the end of our string
247      */
248     while (ri > 0) {
249 	s[i++] = sright[--ri];
250     }
251     free(sright);
252 
253     s[i] = '\0';
254     return(i);
255 }
256 
257 /** ask user to enter a character after reading the message */
258 
259 void
askforchar(void)260 askforchar(void)
261 {
262     addstr("Type any character to continue: ");
263     mygetch();
264 }
265 
266 /** ask user to press the RETURN key after reading the message */
267 
268 void
askforreturn(void)269 askforreturn(void)
270 {
271     fprintf(stderr, "Press the RETURN key to continue: ");
272     getchar();
273     /* HBB 20060419: message probably messed up the screen --- redraw */
274     if (incurses == YES) {
275 	redrawwin(curscr);
276     }
277 }
278 
279 /** expand the ~ and $ shell meta characters in a path */
280 
281 void
shellpath(char * out,int limit,char * in)282 shellpath(char *out, int limit, char *in)
283 {
284     char	*lastchar;
285     char	*s, *v;
286 
287     /* skip leading white space */
288     while (isspace((unsigned char)*in)) {
289 	++in;
290     }
291     lastchar = out + limit - 1;
292 
293     /* a tilde (~) by itself represents $HOME; followed by a name it
294        represents the $LOGDIR of that login name */
295     if (*in == '~') {
296 	*out++ = *in++;	/* copy the ~ because it may not be expanded */
297 
298 	/* get the login name */
299 	s = out;
300 	while (s < lastchar && *in != '/' && *in != '\0' && !isspace((unsigned char)*in)) {
301 	    *s++ = *in++;
302 	}
303 	*s = '\0';
304 
305 	/* if the login name is null, then use $HOME */
306 	if (*out == '\0') {
307 	    v = getenv("HOME");
308 	} else {	/* get the home directory of the login name */
309 	    v = logdir(out);
310 	}
311 	/* copy the directory name if it isn't too big */
312 	if (v != NULL && strlen(v) < (lastchar - out)) {
313 	    strcpy(out - 1, v);
314 	    out += strlen(v) - 1;
315 	} else {
316 	    /* login not found, so ~ must be part of the file name */
317 	    out += strlen(out);
318 	}
319     }
320     /* get the rest of the path */
321     while (out < lastchar && *in != '\0' && !isspace((unsigned char)*in)) {
322 
323 	/* look for an environment variable */
324 	if (*in == '$') {
325 	    *out++ = *in++;	/* copy the $ because it may not be expanded */
326 
327 	    /* get the variable name */
328 	    s = out;
329 	    while (s < lastchar && *in != '/' && *in != '\0' &&
330 		   !isspace((unsigned char)*in)) {
331 		*s++ = *in++;
332 	    }
333 	    *s = '\0';
334 
335 	    /* get its value, but only it isn't too big */
336 	    if ((v = getenv(out)) != NULL && strlen(v) < (lastchar - out)) {
337 		strcpy(out - 1, v);
338 		out += strlen(v) - 1;
339 	    } else {
340 		/* var not found, or too big, so assume $ must be part of the
341 		 * file name */
342 		out += strlen(out);
343 	    }
344 	}
345 	else {	/* ordinary character */
346 	    *out++ = *in++;
347 	}
348     }
349     *out = '\0';
350 }
351