1 /* Based on: "$Id$"; */
2 static const char copyright
[] = "getline: Copyright (C) 1991, 1992, 1993, Chris Thewalt";
5 * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
7 * Permission to use, copy, modify, and distribute this software
8 * for any purpose and without fee is hereby granted, provided
9 * that the above copyright notices appear in all copies and that both the
10 * copyright notice and this permission notice appear in supporting
11 * documentation. This software is provided "as is" without express or
14 * Thanks to the following people who have provided enhancements and fixes:
15 * Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
16 * DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
17 * Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich Betzler
21 * Note: This version has been updated by Mike Gleason <mgleason@ncftp.com>
24 #if defined(WIN32) || defined(_WINDOWS)
26 # include <sys/types.h>
27 # include <sys/stat.h>
32 # define strcasecmp stricmp
33 # define strncasecmp strnicmp
34 # define sleep(a) Sleep(a * 1000)
36 # define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
37 # define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
50 # define utimbuf _utimbuf
53 # define unlink remove
56 # define LOCAL_PATH_DELIM '\\'
57 # define LOCAL_PATH_DELIM_STR "\\"
58 # define LOCAL_PATH_ALTDELIM '/'
59 # define IsLocalPathDelim(c) ((c == LOCAL_PATH_DELIM) || (c == LOCAL_PATH_ALTDELIM))
60 # define UNC_PATH_PREFIX "\\\\"
61 # define IsUNCPrefixed(s) (IsLocalPathDelim(s[0]) && IsLocalPathDelim(s[1]))
62 # define __windows__ 1
67 # if defined(AIX) || defined(_AIX)
68 # define _ALL_SOURCE 1
70 # if defined(HAVE_CONFIG_H)
74 # define HAVE_TERMIOS_H 1
75 # define HAVE_UNISTD_H 1
80 # include <sys/types.h>
81 # include <sys/time.h>
82 # include <sys/stat.h>
83 # ifdef CAN_USE_SYS_SELECT_H
84 # include <sys/select.h>
90 # ifdef HAVE_TERMIOS_H /* use HAVE_TERMIOS_H interface */
92 struct termios new_termios
, old_termios
;
93 # else /* not HAVE_TERMIOS_H */
94 # include <sys/ioctl.h>
95 # ifdef TIOCSETN /* use BSD interface */
97 struct sgttyb new_tty
, old_tty
;
100 # else /* use SYSV interface */
102 struct termio new_termio
, old_termio
;
103 # endif /* TIOCSETN */
104 # endif /* HAVE_TERMIOS_H */
105 # define LOCAL_PATH_DELIM '/'
106 # define LOCAL_PATH_DELIM_STR "/"
107 # define _StrFindLocalPathDelim(a) strchr(a, LOCAL_PATH_DELIM)
108 # define _StrRFindLocalPathDelim(a) strrchr(a, LOCAL_PATH_DELIM)
109 # define IsLocalPathDelim(c) (c == LOCAL_PATH_DELIM)
112 /********************* C library headers ********************************/
116 #ifdef HAVE_STRINGS_H
117 # include <strings.h>
123 #define _getline_c_ 1
126 static int gl_tab(char *buf
, int offset
, int *loc
, size_t bufsize
);
128 /******************** external interface *********************************/
130 gl_in_hook_proc gl_in_hook
= 0;
131 gl_out_hook_proc gl_out_hook
= 0;
132 gl_tab_hook_proc gl_tab_hook
= gl_tab
;
133 gl_strlen_proc gl_strlen
= (gl_strlen_proc
) strlen
;
134 gl_tab_completion_proc gl_completion_proc
= 0;
135 int gl_filename_quoting_desired
= -1; /* default to unspecified */
136 const char *gl_filename_quote_characters
= " \t*?<>|;&()[]$`";
137 int gl_ellipses_during_completion
= 1;
138 int gl_completion_exact_match_extra_char
;
139 char gl_buf
[GL_BUF_SIZE
]; /* input buffer */
141 /******************** internal interface *********************************/
144 static int gl_init_done
= -1; /* terminal mode flag */
145 static int gl_termw
= 80; /* actual terminal width */
146 static int gl_termh
= 24; /* actual terminal height */
147 static int gl_scroll
= 27; /* width of EOL scrolling region */
148 static int gl_width
= 0; /* net size available for input */
149 static int gl_extent
= 0; /* how far to redraw, 0 means all */
150 static int gl_overwrite
= 0; /* overwrite mode */
151 static int gl_pos
= 0, gl_cnt
= 0; /* position and size of input */
152 static char gl_killbuf
[GL_BUF_SIZE
]=""; /* killed text */
153 static const char *gl_prompt
; /* to save the prompt string */
154 static char gl_intrc
= 0; /* keyboard SIGINT char */
155 static char gl_quitc
= 0; /* keyboard SIGQUIT char */
156 static char gl_suspc
= 0; /* keyboard SIGTSTP char */
157 static char gl_dsuspc
= 0; /* delayed SIGTSTP char */
158 static int gl_search_mode
= 0; /* search mode flag */
159 static char **gl_matchlist
= 0;
160 static char *gl_home_dir
= NULL
;
161 static int gl_vi_preferred
= -1;
162 static int gl_vi_mode
= 0;
163 static int gl_result
= GL_OK
;
165 static void gl_init(void); /* prepare to edit a line */
166 static void gl_cleanup(void); /* to undo gl_init */
167 static void gl_char_init(void); /* get ready for no echo input */
168 static void gl_char_cleanup(void); /* undo gl_char_init */
169 /* returns printable prompt width */
171 static void gl_addchar(int c
); /* install specified char */
172 static void gl_del(int loc
, int); /* del, either left (-1) or cur (0) */
173 static void gl_error(const char *const buf
); /* write error msg and die */
174 static void gl_fixup(const char *prompt
, int change
, int cursor
); /* fixup state variables and screen */
175 static int gl_getc(void); /* read one char from terminal */
176 static int gl_getcx(int); /* read one char from terminal, if available before timeout */
177 static void gl_kill(int pos
); /* delete to EOL */
178 static void gl_newline(void); /* handle \n or \r */
179 static void gl_putc(int c
); /* write one char to terminal */
180 static void gl_puts(const char *const buf
); /* write a line to terminal */
181 static void gl_redraw(void); /* issue \n and redraw all */
182 static void gl_transpose(void); /* transpose two chars */
183 static void gl_yank(void); /* yank killed text */
184 static void gl_word(int direction
); /* move a word */
185 static void gl_killword(int direction
);
187 static void hist_init(void); /* initializes hist pointers */
188 static char *hist_next(void); /* return ptr to next item */
189 static char *hist_prev(void); /* return ptr to prev item */
190 static char *hist_save(char *p
); /* makes copy of a string, without NL */
192 static void search_addchar(int c
); /* increment search string */
193 static void search_term(void); /* reset with current contents */
194 static void search_back(int new_search
); /* look back for current string */
195 static void search_forw(int new_search
); /* look forw for current string */
196 static void gl_beep(void); /* try to play a system beep sound */
198 static int gl_do_tab_completion(char *buf
, int *loc
, size_t bufsize
, int tabtab
);
200 /************************ nonportable part *********************************/
207 gl_char_init(void) /* turn off input echo */
210 # ifdef HAVE_TERMIOS_H /* Use POSIX */
211 if (tcgetattr(0, &old_termios
) == 0) {
212 gl_intrc
= old_termios
.c_cc
[VINTR
];
213 gl_quitc
= old_termios
.c_cc
[VQUIT
];
215 gl_suspc
= old_termios
.c_cc
[VSUSP
];
218 gl_dsuspc
= old_termios
.c_cc
[VDSUSP
];
221 new_termios
= old_termios
;
222 new_termios
.c_iflag
&= ~(BRKINT
|ISTRIP
|IXON
|IXOFF
);
223 new_termios
.c_iflag
|= (IGNBRK
|IGNPAR
);
224 new_termios
.c_lflag
&= ~(ICANON
|ISIG
|IEXTEN
|ECHO
);
225 new_termios
.c_cc
[VMIN
] = 1;
226 new_termios
.c_cc
[VTIME
] = 0;
227 tcsetattr(0, TCSANOW
, &new_termios
);
228 # elif defined(TIOCSETN) /* BSD */
229 if (ioctl(0, TIOCGETC
, &tch
) == 0) {
230 gl_intrc
= tch
.t_intrc
;
231 gl_quitc
= tch
.t_quitc
;
233 ioctl(0, TIOCGLTC
, <ch
);
234 gl_suspc
= ltch
.t_suspc
;
235 gl_dsuspc
= ltch
.t_dsuspc
;
236 ioctl(0, TIOCGETP
, &old_tty
);
238 new_tty
.sg_flags
|= RAW
;
239 new_tty
.sg_flags
&= ~ECHO
;
240 ioctl(0, TIOCSETN
, &new_tty
);
242 if (ioctl(0, TCGETA
, &old_termio
) == 0) {
243 gl_intrc
= old_termio
.c_cc
[VINTR
];
244 gl_quitc
= old_termio
.c_cc
[VQUIT
];
246 new_termio
= old_termio
;
247 new_termio
.c_iflag
&= ~(BRKINT
|ISTRIP
|IXON
|IXOFF
);
248 new_termio
.c_iflag
|= (IGNBRK
|IGNPAR
);
249 new_termio
.c_lflag
&= ~(ICANON
|ISIG
|ECHO
);
250 new_termio
.c_cc
[VMIN
] = 1;
251 new_termio
.c_cc
[VTIME
] = 0;
252 ioctl(0, TCSETA
, &new_termio
);
254 #endif /* __unix__ */
258 gl_char_cleanup(void) /* undo effects of gl_char_init */
261 # ifdef HAVE_TERMIOS_H
262 tcsetattr(0, TCSANOW
, &old_termios
);
263 # elif defined(TIOCSETN) /* BSD */
264 ioctl(0, TIOCSETN
, &old_tty
);
266 ioctl(0, TCSETA
, &old_termio
);
268 #endif /* __unix__ */
277 } /* gl_get_result */
282 #if defined(MSDOS) || defined(__windows__)
288 #define K_DELETE 0x53
289 #define K_INSERT 0x52
300 c
= 16; /* up -> ^P */
304 c
= 14; /* down -> ^N */
307 c
= 2; /* left -> ^B */
310 c
= 6; /* right -> ^F */
313 c
= 5; /* end -> ^E */
316 c
= 1; /* home -> ^A */
319 c
= 15; /* insert -> ^O */
322 c
= 4; /* del -> ^D */
325 c
= 0; /* make it garbage */
329 #endif /* defined(MSDOS) || defined(__windows__) */
333 /* get a character without echoing it to screen */
342 while ((c
= (int) read(0, &ch
, 1)) == -1) {
348 #endif /* __unix__ */
350 c
= _bios_keybrd(_NKEYBRD_READ
);
351 if ((c
& 0377) == 224) {
352 c
= pc_keymap((c
>> 8) & 0377);
359 if ((c
== 0) || (c
== 0xE0)) {
363 } else if (c
== '\r') {
364 /* Note: we only get \r from the console,
365 * and not a matching \n.
379 /* Get a character without echoing it to screen, timing out
380 * after tlen tenths of a second.
390 FD_SET(0, &ss
); /* set STDIN_FILENO */
391 tv
.tv_sec
= tlen
/ 10;
392 tv
.tv_usec
= (tlen
% 10) * 100000L;
393 result
= select(1, &ss
, NULL
, NULL
, &tv
);
397 } else if (result
== 0) {
400 } else if (errno
!= EINTR
) {
406 c
= (int) read(0, &ch
, 1);
416 #endif /* __unix__ */
429 tlen
-= 2; /* Adjust for 200ms overhead */
432 for (i
=0; i
<tlen
; i
++) {
435 if ((c
== 0) || (c
== 0xE0)) {
442 (void) SleepEx((DWORD
) (tlen
* 100), FALSE
);
447 #endif /* __windows__ */
455 char ch
= (char) (unsigned char) c
;
460 write(1, &ch
, 1); /* RAW mode needs '\r', does not hurt */
464 /******************** fairly portable part *********************************/
467 gl_puts(const char *const buf
)
472 len
= (int) strlen(buf
);
478 gl_error(const char *const buf
)
480 int len
= (int) strlen(buf
);
489 /* set up variables and terminal */
494 if (gl_init_done
< 0) { /* -1 only on startup */
495 cp
= (const char *) getenv("COLUMNS");
501 cp
= (const char *) getenv("ROWS");
509 if (isatty(0) == 0 || isatty(1) == 0)
510 gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
517 /* undo effects of gl_init, as necessary */
519 if (gl_init_done
> 0)
524 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
530 gl_check_inputrc_for_vi(void)
535 /* If the user has a ~/.inputrc file,
536 * check it to see if it has a line like
537 * "set editing-mode vi". If it does,
538 * we know that the user wants vi
539 * emulation rather than emacs. If the
540 * file doesn't exist, it's no big
541 * deal since we can also check the
542 * $EDITOR environment variable.
544 gl_set_home_dir(NULL
);
545 if (gl_home_dir
== NULL
)
549 snprintf(path
, sizeof(path
), "%s/%s", gl_home_dir
, ".inputrc");
551 if (sizeof(path
) >= (strlen(gl_home_dir
) + strlen("/.inputrc")))
554 sprintf(path
, "%s%s", gl_home_dir
, "/.inputrc");
559 #if defined(__windows__) || defined(MSDOS)
569 while (fgets(path
, sizeof(path
) - 1, fp
) != NULL
) {
570 if ((strstr(path
, "editing-mode") != NULL
) && (strstr(path
, "vi") != NULL
)) {
577 } /* gl_check_inputrc_for_vi */
590 gl_error("\n*** Error: minimum screen width is 21\n");
602 gl_error("\n*** Error: minimum screen height is 10\n");
610 getline(char *prompt
)
612 int c
, loc
, tmp
, lastch
;
615 char vi_countbuf
[32];
622 /* We'll change the result code only if something happens later. */
625 /* Even if it appears that "vi" is preferred, we
626 * don't start in gl_vi_mode. They need to hit
627 * ESC to go into vi command mode.
632 if (gl_vi_preferred
< 0) {
634 cp
= (char *) getenv("EDITOR");
636 gl_vi_preferred
= (strstr(cp
, "vi") != NULL
);
637 if (gl_vi_preferred
== 0)
638 gl_check_inputrc_for_vi();
642 gl_prompt
= (prompt
)? prompt
: "";
646 gl_fixup(gl_prompt
, -2, GL_BUF_SIZE
);
650 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
653 while ((c
= gl_getc()) != (-1)) {
654 gl_extent
= 0; /* reset to full extent */
655 /* Note: \n may or may not be considered printable */
656 if ((c
!= '\t') && ((isprint(c
) != 0) || ((c
& 0x80) != 0))) {
657 if (gl_vi_mode
> 0) {
658 /* "vi" emulation -- far from perfect,
659 * but reasonably functional.
662 for (count
= 0; ; ) {
664 if (vi_countbuf
[sizeof(vi_countbuf
) - 2] == '\0')
665 vi_countbuf
[strlen(vi_countbuf
)] = (char) c
;
666 } else if (vi_countbuf
[0] != '\0') {
667 vi_count
= atoi(vi_countbuf
);
668 memset(vi_countbuf
, 0, sizeof(vi_countbuf
));
684 gl_fixup(gl_prompt
, -1, gl_pos
-1);
688 gl_fixup(gl_prompt
, -1, gl_pos
-1);
692 case 'l': /* right */
696 gl_fixup(gl_prompt
, -1, gl_pos
+1);
700 strcpy(gl_buf
, hist_prev());
703 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
706 strcpy(gl_buf
, hist_next());
709 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
712 if (vi_delete
== 1) {
721 case '^': /* start of line */
724 gl_fixup(gl_prompt
, -1, 0);
725 for (c
= 0; c
< vi_count
; c
++) {
732 gl_fixup(gl_prompt
, -1, 0);
735 case '$': /* end of line */
739 loc
= (int) strlen(gl_buf
);
742 gl_fixup(gl_prompt
, -1, loc
);
745 case 'p': /* paste after */
746 gl_fixup(gl_prompt
, -1, gl_pos
+1);
749 case 'P': /* paste before */
752 case 'r': /* replace character */
753 gl_buf
[gl_pos
] = (char) gl_getc();
754 gl_fixup(gl_prompt
, gl_pos
, gl_pos
);
771 gl_fixup(gl_prompt
, -1, gl_pos
+1);
776 if (count
>= vi_count
)
783 } else if (gl_search_mode
) {
789 if (gl_search_mode
) {
790 if (c
== '\033' || c
== '\016' || c
== '\020') {
792 c
= 0; /* ignore the character */
793 } else if (c
== '\010' || c
== '\177') {
794 search_addchar(-1); /* unwind search string */
796 } else if (c
!= '\022' && c
!= '\023') {
797 search_term(); /* terminate and handle char */
801 case '\n': case '\r': /* newline */
805 case '\001': gl_fixup(gl_prompt
, -1, 0); /* ^A */
807 case '\002': gl_fixup(gl_prompt
, -1, gl_pos
-1); /* ^B */
809 case '\004': /* ^D */
820 case '\005': gl_fixup(gl_prompt
, -1, gl_cnt
); /* ^E */
822 case '\006': gl_fixup(gl_prompt
, -1, gl_pos
+1); /* ^F */
824 case '\010': case '\177': gl_del(-1, 0); /* ^H and DEL */
827 if (gl_completion_proc
) {
829 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
830 loc
= gl_do_tab_completion(gl_buf
, &tmp
, sizeof(gl_buf
), (lastch
== '\t'));
831 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
832 if (loc
>= 0 || tmp
!= gl_pos
)
833 gl_fixup(gl_prompt
, /* loc */ -2, tmp
);
834 if (lastch
== '\t') {
838 } else if (gl_tab_hook
) {
840 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
841 loc
= gl_tab_hook(gl_buf
, (int) gl_strlen(gl_prompt
), &tmp
, sizeof(gl_buf
));
842 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
843 if (loc
>= 0 || tmp
!= gl_pos
)
844 gl_fixup(gl_prompt
, loc
, tmp
);
847 case '\013': gl_kill(gl_pos
); /* ^K */
849 case '\014': gl_redraw(); /* ^L */
851 case '\016': /* ^N */
852 strcpy(gl_buf
, hist_next());
855 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
857 case '\017': gl_overwrite
= !gl_overwrite
; /* ^O */
859 case '\020': /* ^P */
860 strcpy(gl_buf
, hist_prev());
863 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
865 case '\022': search_back(1); /* ^R */
867 case '\023': search_forw(1); /* ^S */
869 case '\024': gl_transpose(); /* ^T */
871 case '\025': gl_kill(0); /* ^U */
873 case '\027': gl_killword(-1); /* ^W */
875 case '\031': gl_yank(); /* ^Y */
877 case '\033': /* ansi arrow keys */
879 if ((c
== '[') || (c
== 'O')) {
881 switch(c
= gl_getc()) {
883 strcpy(gl_buf
, hist_prev());
886 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
889 strcpy(gl_buf
, hist_next());
892 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
895 gl_fixup(gl_prompt
, -1, gl_pos
+1); /* right */
898 gl_fixup(gl_prompt
, -1, gl_pos
-1); /* left */
903 default: gl_beep(); /* who knows */
906 } else if ((gl_vi_preferred
== 0) && ((c
== 'f') || (c
== 'F'))) {
908 } else if ((gl_vi_preferred
== 0) && ((c
== 'b') || (c
== 'B'))) {
910 } else if (c
!= (-1)) {
911 /* enter vi command mode */
912 #if defined(__windows__) || defined(MSDOS)
913 if (gl_vi_preferred
== 0) {
914 /* On Windows, ESC acts like a line kill,
915 * so don't use vi mode unless they prefer
921 if (gl_vi_mode
== 0) {
925 memset(vi_countbuf
, 0, sizeof(vi_countbuf
));
927 gl_fixup(gl_prompt
, -2, gl_pos
-1); /* left 1 char */
928 /* Don't bother if the line is empty and we don't
929 * know for sure if the user wants vi mode.
931 if ((gl_cnt
> 0) || (gl_vi_preferred
== 1)) {
932 /* We still have to use the char read! */
941 default: /* check for a terminal signal */
942 if (c
> 0) { /* ignore 0 (reset above) */
944 gl_result
= GL_INTERRUPT
;
956 gl_result
= GL_INTERRUPT
;
968 if (c
== gl_suspc
|| c
== gl_dsuspc
) {
970 gl_result
= GL_INTERRUPT
;
980 #endif /* __unix__ */
998 /* adds the character c to the input buffer at current location */
1002 if (gl_cnt
>= GL_BUF_SIZE
- 1)
1003 gl_error("\n*** Error: getline(): input buffer overflow\n");
1004 if (gl_overwrite
== 0 || gl_pos
== gl_cnt
) {
1005 for (i
=gl_cnt
; i
>= gl_pos
; i
--)
1006 gl_buf
[i
+1] = gl_buf
[i
];
1007 gl_buf
[gl_pos
] = (char) c
;
1008 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+1);
1010 gl_buf
[gl_pos
] = (char) c
;
1012 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+1);
1018 /* adds the kill buffer to the input buffer at current location */
1022 len
= (int) strlen(gl_killbuf
);
1024 if (gl_overwrite
== 0) {
1025 if (gl_cnt
+ len
>= GL_BUF_SIZE
- 1)
1026 gl_error("\n*** Error: getline(): input buffer overflow\n");
1027 for (i
=gl_cnt
; i
>= gl_pos
; i
--)
1028 gl_buf
[i
+len
] = gl_buf
[i
];
1029 for (i
=0; i
< len
; i
++)
1030 gl_buf
[gl_pos
+i
] = gl_killbuf
[i
];
1031 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+len
);
1033 if (gl_pos
+ len
> gl_cnt
) {
1034 if (gl_pos
+ len
>= GL_BUF_SIZE
- 1)
1035 gl_error("\n*** Error: getline(): input buffer overflow\n");
1036 gl_buf
[gl_pos
+ len
] = 0;
1038 for (i
=0; i
< len
; i
++)
1039 gl_buf
[gl_pos
+i
] = gl_killbuf
[i
];
1041 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+len
);
1049 /* switch character under cursor and to left of cursor */
1053 if (gl_pos
> 0 && gl_cnt
> gl_pos
) {
1054 c
= gl_buf
[gl_pos
-1];
1055 gl_buf
[gl_pos
-1] = gl_buf
[gl_pos
];
1056 gl_buf
[gl_pos
] = (char) c
;
1058 gl_fixup(gl_prompt
, gl_pos
-1, gl_pos
);
1066 * Cleans up entire line before returning to caller. A \n is appended.
1067 * If line longer than screen, we redraw starting at beginning
1070 int change
= gl_cnt
;
1072 int loc
= gl_width
- 5; /* shifts line back to start position */
1074 if (gl_cnt
>= GL_BUF_SIZE
- 1)
1075 gl_error("\n*** Error: getline(): input buffer overflow\n");
1077 change
= gl_out_hook(gl_buf
);
1078 len
= (int) strlen(gl_buf
);
1082 gl_fixup(gl_prompt
, change
, loc
); /* must do this before appending \n */
1084 gl_buf
[len
+1] = '\0';
1089 gl_del(int loc
, int killsave
)
1092 * Delete a character. The loc variable can be:
1093 * -1 : delete character to left of cursor
1094 * 0 : delete character under cursor
1099 if ((loc
== -1 && gl_pos
> 0) || (loc
== 0 && gl_pos
< gl_cnt
)) {
1100 for (j
=0, i
=gl_pos
+loc
; i
< gl_cnt
; i
++) {
1101 if ((j
== 0) && (killsave
!= 0) && (gl_vi_mode
!= 0)) {
1102 gl_killbuf
[0] = gl_buf
[i
];
1103 gl_killbuf
[1] = '\0';
1106 gl_buf
[i
] = gl_buf
[i
+1];
1108 gl_fixup(gl_prompt
, gl_pos
+loc
, gl_pos
+loc
);
1116 /* delete from pos to the end of line */
1119 strcpy(gl_killbuf
, gl_buf
+ pos
);
1121 gl_fixup(gl_prompt
, pos
, pos
);
1127 gl_killword(int direction
)
1130 int startpos
= gl_pos
;
1134 if (direction
> 0) { /* forward */
1135 while (!isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1137 while (isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1139 } else { /* backward */
1142 while (isspace(gl_buf
[pos
]) && pos
> 0)
1144 while (!isspace(gl_buf
[pos
]) && pos
> 0)
1146 if (pos
< gl_cnt
&& isspace(gl_buf
[pos
])) /* move onto word */
1149 if (pos
< startpos
) {
1154 memcpy(gl_killbuf
, gl_buf
+ startpos
, (size_t) (pos
- startpos
));
1155 gl_killbuf
[pos
- startpos
] = '\0';
1156 if (isspace(gl_killbuf
[pos
- startpos
- 1]))
1157 gl_killbuf
[pos
- startpos
- 1] = '\0';
1158 gl_fixup(gl_prompt
, -1, startpos
);
1159 for (i
=0, tmp
=pos
- startpos
; i
<tmp
; i
++)
1164 gl_word(int direction
)
1166 /* move forward or backword one word */
1170 if (direction
> 0) { /* forward */
1171 while (!isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1173 while (isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1175 } else { /* backword */
1178 while (isspace(gl_buf
[pos
]) && pos
> 0)
1180 while (!isspace(gl_buf
[pos
]) && pos
> 0)
1182 if (pos
< gl_cnt
&& isspace(gl_buf
[pos
])) /* move onto word */
1185 gl_fixup(gl_prompt
, -1, pos
);
1190 /* emit a newline, reset and redraw prompt and current input line */
1192 if (gl_init_done
> 0) {
1194 gl_fixup(gl_prompt
, -2, gl_pos
);
1199 gl_fixup(const char *prompt
, int change
, int cursor
)
1203 * This function is used both for redrawing when input changes or for
1204 * moving within the input line. The parameters are:
1205 * prompt: compared to last_prompt[] for changes;
1206 * change : the index of the start of changes in the input buffer,
1207 * with -1 indicating no changes, -2 indicating we're on
1208 * a new line, redraw everything.
1209 * cursor : the desired location of the cursor after the call.
1210 * A value of GL_BUF_SIZE can be used to indicate the cursor should
1211 * move just past the end of the input line.
1214 static int gl_shift
; /* index of first on screen character */
1215 static int off_right
; /* true if more text right of screen */
1216 static int off_left
; /* true if more text left of screen */
1217 static char last_prompt
[80] = "";
1218 int left
= 0, right
= -1; /* bounds for redraw */
1219 int pad
; /* how much to erase at end of line */
1220 int backup
; /* how far to backup before fixing */
1221 int new_shift
; /* value of shift based on cursor */
1222 int extra
; /* adjusts when shift (scroll) happens */
1224 int new_right
= -1; /* alternate right bound, using gl_extent */
1227 if (change
== -2) { /* reset */
1228 gl_pos
= gl_cnt
= gl_shift
= off_right
= off_left
= 0;
1231 strcpy(last_prompt
, prompt
);
1233 gl_width
= gl_termw
- (int) gl_strlen(prompt
);
1234 } else if (strcmp(prompt
, last_prompt
) != 0) {
1235 l1
= (int) gl_strlen(last_prompt
);
1236 l2
= (int) gl_strlen(prompt
);
1237 gl_cnt
= gl_cnt
+ l1
- l2
;
1238 strcpy(last_prompt
, prompt
);
1242 gl_width
= gl_termw
- l2
;
1245 pad
= (off_right
)? gl_width
- 1 : gl_cnt
- gl_shift
; /* old length */
1246 backup
= gl_pos
- gl_shift
;
1248 gl_cnt
= (int) strlen(gl_buf
);
1249 if (change
> gl_cnt
)
1252 if (cursor
> gl_cnt
) {
1253 if (cursor
!= GL_BUF_SIZE
) { /* GL_BUF_SIZE means end of line */
1254 if (gl_ellipses_during_completion
== 0) {
1264 if (off_right
|| (off_left
&& cursor
< gl_shift
+ gl_width
- gl_scroll
/ 2))
1265 extra
= 2; /* shift the scrolling boundary */
1268 new_shift
= cursor
+ extra
+ gl_scroll
- gl_width
;
1269 if (new_shift
> 0) {
1270 new_shift
/= gl_scroll
;
1271 new_shift
*= gl_scroll
;
1274 if (new_shift
!= gl_shift
) { /* scroll occurs */
1275 gl_shift
= new_shift
;
1276 off_left
= (gl_shift
)? 1 : 0;
1277 off_right
= (gl_cnt
> gl_shift
+ gl_width
- 1)? 1 : 0;
1279 new_right
= right
= (off_right
)? gl_shift
+ gl_width
- 2 : gl_cnt
;
1280 } else if (change
>= 0) { /* no scroll, but text changed */
1281 if (change
< gl_shift
+ off_left
) {
1285 backup
= gl_pos
- change
;
1287 off_right
= (gl_cnt
> gl_shift
+ gl_width
- 1)? 1 : 0;
1288 right
= (off_right
)? gl_shift
+ gl_width
- 2 : gl_cnt
;
1289 new_right
= (gl_extent
&& (right
> left
+ gl_extent
))?
1290 left
+ gl_extent
: right
;
1292 pad
-= (off_right
)? gl_width
- 1 : gl_cnt
- gl_shift
;
1293 pad
= (pad
< 0)? 0 : pad
;
1294 if (left
<= right
) { /* clean up screen */
1295 for (i
=0; i
< backup
; i
++)
1297 if (left
== gl_shift
&& off_left
) {
1301 for (i
=left
; i
< new_right
; i
++)
1304 if (off_right
&& new_right
== right
) {
1308 for (i
=0; i
< pad
; i
++) /* erase remains of prev line */
1313 i
= gl_pos
- cursor
; /* move to final cursor location */
1318 for (i
=gl_pos
; i
< cursor
; i
++)
1325 gl_tab(char *buf
, int offset
, int *loc
, size_t bufsize
)
1326 /* default tab handler, acts like tabstops every 8 cols */
1330 len
= (int) strlen(buf
);
1331 count
= 8 - (offset
+ *loc
) % 8;
1332 for (i
=len
; i
>= *loc
; i
--)
1333 if (i
+count
< (int) bufsize
)
1334 buf
[i
+count
] = buf
[i
];
1335 for (i
=0; i
< count
; i
++)
1336 if (*loc
+i
< (int) bufsize
)
1343 /******************* History stuff **************************************/
1346 #define HIST_SIZE 100
1349 static int hist_pos
= 0, hist_last
= 0;
1350 static char *hist_buf
[HIST_SIZE
];
1351 static char hist_empty_elem
[2] = "";
1358 hist_buf
[0] = hist_empty_elem
;
1359 for (i
=1; i
< HIST_SIZE
; i
++)
1360 hist_buf
[i
] = (char *)0;
1364 gl_histadd(char *buf
)
1366 static char *prev
= 0;
1370 /* in case we call gl_histadd() before we call getline() */
1371 if (gl_init_done
< 0) { /* -1 only on startup */
1375 while (*p
== ' ' || *p
== '\t' || *p
== '\n')
1378 len
= (int) strlen(buf
);
1379 if (strchr(p
, '\n')) /* previously line already has NL stripped */
1381 if ((prev
== 0) || ((int) strlen(prev
) != len
) ||
1382 strncmp(prev
, buf
, (size_t) len
) != 0) {
1383 hist_buf
[hist_last
] = hist_save(buf
);
1384 prev
= hist_buf
[hist_last
];
1385 hist_last
= (hist_last
+ 1) % HIST_SIZE
;
1386 if (hist_buf
[hist_last
] && *hist_buf
[hist_last
]) {
1387 free(hist_buf
[hist_last
]);
1389 hist_buf
[hist_last
] = hist_empty_elem
;
1392 hist_pos
= hist_last
;
1397 /* loads previous hist entry into input buffer, sticks on first */
1400 int next
= (hist_pos
- 1 + HIST_SIZE
) % HIST_SIZE
;
1402 if (hist_buf
[hist_pos
] != 0 && next
!= hist_last
) {
1404 p
= hist_buf
[hist_pos
];
1407 p
= hist_empty_elem
;
1415 /* loads next hist entry into input buffer, clears on last */
1419 if (hist_pos
!= hist_last
) {
1420 hist_pos
= (hist_pos
+1) % HIST_SIZE
;
1421 p
= hist_buf
[hist_pos
];
1424 p
= hist_empty_elem
;
1433 /* makes a copy of the string */
1436 size_t len
= strlen(p
);
1437 char *nl
= strpbrk(p
, "\n\r");
1440 if ((s
= (char *) malloc(len
)) != 0) {
1441 strncpy(s
, p
, len
-1);
1445 if ((s
= (char *) malloc(len
+1)) != 0) {
1450 gl_error("\n*** Error: hist_save() failed on malloc\n");
1458 gl_histsavefile(const char *const path
)
1465 #if defined(__windows__) || defined(MSDOS)
1472 for (i
=2; i
<HIST_SIZE
; i
++) {
1473 j
= (hist_pos
+i
) % HIST_SIZE
;
1475 if ((p
== NULL
) || (*p
== '\0'))
1477 fprintf(fp
, "%s\n", p
);
1481 } /* gl_histsavefile */
1487 gl_histloadfile(const char *const path
)
1493 #if defined(__windows__) || defined(MSDOS)
1500 memset(line
, 0, sizeof(line
));
1501 while (fgets(line
, sizeof(line
) - 2, fp
) != NULL
) {
1506 } /* gl_histloadfile */
1511 /******************* Search stuff **************************************/
1513 static char search_prompt
[101]; /* prompt includes search string */
1514 static char search_string
[100];
1515 static int search_pos
= 0; /* current location in search_string */
1516 static int search_forw_flg
= 0; /* search direction flag */
1517 static int search_last
= 0; /* last match found */
1520 search_update(int c
)
1524 search_string
[0] = 0;
1525 search_prompt
[0] = '?';
1526 search_prompt
[1] = ' ';
1527 search_prompt
[2] = 0;
1529 search_string
[search_pos
] = (char) c
;
1530 search_string
[search_pos
+1] = (char) 0;
1531 search_prompt
[search_pos
] = (char) c
;
1532 search_prompt
[search_pos
+1] = (char) '?';
1533 search_prompt
[search_pos
+2] = (char) ' ';
1534 search_prompt
[search_pos
+3] = (char) 0;
1537 if (search_pos
> 0) {
1539 search_string
[search_pos
] = (char) 0;
1540 search_prompt
[search_pos
] = (char) '?';
1541 search_prompt
[search_pos
+1] = (char) ' ';
1542 search_prompt
[search_pos
+2] = (char) 0;
1545 hist_pos
= hist_last
;
1551 search_addchar(int c
)
1557 if (search_pos
> 0) {
1558 hist_pos
= search_last
;
1561 hist_pos
= hist_last
;
1563 strcpy(gl_buf
, hist_buf
[hist_pos
]);
1565 if ((loc
= strstr(gl_buf
, search_string
)) != 0) {
1566 gl_fixup(search_prompt
, 0, (int) (loc
- gl_buf
));
1567 } else if (search_pos
> 0) {
1568 if (search_forw_flg
) {
1574 gl_fixup(search_prompt
, 0, 0);
1582 if (gl_buf
[0] == 0) /* not found, reset hist list */
1583 hist_pos
= hist_last
;
1586 gl_fixup(gl_prompt
, 0, gl_pos
);
1590 search_back(int new_search
)
1595 search_forw_flg
= 0;
1596 if (gl_search_mode
== 0) {
1597 search_last
= hist_pos
= hist_last
;
1601 gl_fixup(search_prompt
, 0, 0);
1602 } else if (search_pos
> 0) {
1605 if (*p
== 0) { /* not found, done looking */
1607 gl_fixup(search_prompt
, 0, 0);
1609 } else if ((loc
= strstr(p
, search_string
)) != 0) {
1611 gl_fixup(search_prompt
, 0, (int) (loc
- p
));
1613 search_last
= hist_pos
;
1624 search_forw(int new_search
)
1629 search_forw_flg
= 1;
1630 if (gl_search_mode
== 0) {
1631 search_last
= hist_pos
= hist_last
;
1635 gl_fixup(search_prompt
, 0, 0);
1636 } else if (search_pos
> 0) {
1639 if (*p
== 0) { /* not found, done looking */
1641 gl_fixup(search_prompt
, 0, 0);
1643 } else if ((loc
= strstr(p
, search_string
)) != 0) {
1645 gl_fixup(search_prompt
, 0, (int) (loc
- p
));
1647 search_last
= hist_pos
;
1670 gl_display_matches_sort_proc(const void *a
, const void *b
)
1673 * ((const char **) a
),
1674 * ((const char **) b
)
1676 } /* gl_display_matches_sort_proc */
1681 gl_display_matches(int nused
)
1685 size_t ilen
, imaxlen
;
1688 int nmax
, ncol
, colw
, nrow
;
1689 char *cp1
, *cp2
, *lim
, *itemp
;
1694 gl_puts(" (no matches)");
1697 qsort(gl_matchlist
, (size_t) nused
, sizeof(char *), gl_display_matches_sort_proc
);
1699 /* Find the greatest amount that matches. */
1700 for (glen
= 0; ; glen
++) {
1702 for (i
=1; i
<nused
; i
++) {
1703 if (gl_matchlist
[0][glen
] != gl_matchlist
[i
][glen
]) {
1713 if (!isalnum(gl_matchlist
[0][glen
- 1]))
1719 imaxlen
= strlen(gl_matchlist
[0]);
1720 for (i
=1; i
<nused
; i
++) {
1721 ilen
= strlen(gl_matchlist
[i
]);
1726 /* Subtract amount we'll skip for each item. */
1729 ncol
= (gl_termw
- 8) / ((int) imaxlen
+ 2);
1733 colw
= (gl_termw
- 8) / ncol
;
1735 if ((nused
% ncol
) != 0)
1738 if (nrow
> (gl_termh
- 4)) {
1739 nrow
= gl_termh
- 4;
1743 for (i
=0; i
<(int) sizeof(buf2
); i
++)
1746 for (j
=0; j
<nrow
; j
++) {
1747 (void) memcpy(buf
, buf2
, sizeof(buf
));
1748 for (i
=0, k
=j
, l
=4; i
<ncol
; i
++, k
+= nrow
, l
+= colw
) {
1751 itemp
= gl_matchlist
[k
] + glen
;
1753 lim
= cp1
+ (int) strlen(itemp
);
1754 if (lim
> (buf
+ sizeof(buf
) - 1))
1760 for (cp1
= buf
+ sizeof(buf
); *--cp1
== ' '; )
1769 (void) sprintf(buf
, " ... %d others omitted ...", (nused
- nmax
));
1774 gl_fixup(gl_prompt
, -2, GL_BUF_SIZE
);
1775 } /* gl_display_matches */
1781 gl_do_tab_completion(char *buf
, int *loc
, size_t bufsize
, int tabtab
)
1784 size_t startoff
, amt
;
1788 char *lastspacestart
;
1790 int ntoalloc
, nused
, nprocused
, nalloced
, i
;
1791 char **newgl_matchlist
;
1792 char *strtoadd
, *strtoadd1
;
1794 size_t llen
, mlen
, glen
;
1797 size_t lenaftercursor
;
1800 char ellipsessave
[4];
1802 /* Zero out the rest of the buffer, so we can move stuff around
1803 * and know we'll still be NUL-terminated.
1806 memset(buf
+ llen
, 0, bufsize
- llen
);
1807 bufsize
-= 4; /* leave room for a NUL, space, and two quotes. */
1808 curposp
= buf
+ *loc
;
1809 wasateol
= (*curposp
== '\0');
1810 lenaftercursor
= llen
- (curposp
- buf
);
1811 if (gl_ellipses_during_completion
!= 0) {
1812 memcpy(ellipsessave
, curposp
, (size_t) 4);
1813 memcpy(curposp
, "... ", (size_t) 4);
1814 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+ 3);
1815 memcpy(curposp
, ellipsessave
, (size_t) 4);
1820 lastspacestart
= NULL
;
1824 while (cp
< curposp
) {
1828 if ((c
== '"') || (c
== '\'')) {
1830 /* closing quote; end it. */
1833 } else if (qmode
!= 0) {
1834 /* just treat it as a regular char. */
1836 /* start new quote group. */
1840 } else if ((isspace(c
)) && (qmode
== 0)) {
1841 /* found a non-quoted space. */
1842 lastspacestart
= cp
- 1;
1849 startp
= qstart
+ 1;
1850 else if (lastspacestart
!= NULL
)
1851 startp
= lastspacestart
+ 1;
1856 mlen
= (curposp
- cp
);
1858 matchpfx
= (char *) malloc(mlen
+ 1);
1859 memcpy(matchpfx
, cp
, mlen
);
1860 matchpfx
[mlen
] = '\0';
1862 #define GL_COMPLETE_VECTOR_BLOCK_SIZE 64
1865 ntoalloc
= GL_COMPLETE_VECTOR_BLOCK_SIZE
;
1866 newgl_matchlist
= (char **) malloc((size_t) (sizeof(char *) * (ntoalloc
+ 1)));
1867 if (newgl_matchlist
== NULL
) {
1872 gl_matchlist
= newgl_matchlist
;
1873 nalloced
= ntoalloc
;
1874 for (i
=nused
; i
<=nalloced
; i
++)
1875 gl_matchlist
[i
] = NULL
;
1877 gl_completion_exact_match_extra_char
= ' ';
1878 for (nprocused
= 0;; nprocused
++) {
1879 if (nused
== nalloced
) {
1880 ntoalloc
+= GL_COMPLETE_VECTOR_BLOCK_SIZE
;
1881 newgl_matchlist
= (char **) realloc((char *) gl_matchlist
, (size_t) (sizeof(char *) * (ntoalloc
+ 1)));
1882 if (newgl_matchlist
== NULL
) {
1883 /* not enough memory to expand list -- abort */
1884 for (i
=0; i
<nused
; i
++)
1885 free(gl_matchlist
[i
]);
1887 gl_matchlist
= NULL
;
1892 gl_matchlist
= newgl_matchlist
;
1893 nalloced
= ntoalloc
;
1894 for (i
=nused
; i
<=nalloced
; i
++)
1895 gl_matchlist
[i
] = NULL
;
1897 cp
= gl_completion_proc(matchpfx
, nprocused
);
1900 if ((cp
[0] == '.') && ((cp
[1] == '\0') || ((cp
[1] == '.') && (cp
[2] == '\0'))))
1901 continue; /* Skip . and .. */
1902 gl_matchlist
[nused
++] = cp
;
1905 if (gl_ellipses_during_completion
!= 0) {
1906 gl_fixup(gl_prompt
, gl_pos
, gl_pos
);
1910 /* We now have an array strings, whose last element is NULL. */
1915 addquotes
= (gl_filename_quoting_desired
> 0) || ((gl_filename_quoting_desired
< 0) && (gl_completion_proc
== gl_local_filename_completion_proc
));
1918 /* Exactly one match. */
1919 strtoadd
= gl_matchlist
[0];
1920 } else if (tabtab
!= 0) {
1921 /* TAB-TAB: print all matches */
1922 gl_display_matches(nused
);
1923 } else if ((nused
> 1) && (mlen
> 0)) {
1924 /* Find the greatest amount that matches. */
1925 for (glen
= strlen(matchpfx
); ; glen
++) {
1927 for (i
=1; i
<nused
; i
++) {
1928 if (gl_matchlist
[0][glen
] != gl_matchlist
[i
][glen
]) {
1936 strtoadd1
= (char *) malloc(glen
+ 1);
1937 if (strtoadd1
!= NULL
) {
1938 memcpy(strtoadd1
, gl_matchlist
[0], glen
);
1939 strtoadd1
[glen
] = '\0';
1940 strtoadd
= strtoadd1
;
1944 if (strtoadd
!= NULL
) {
1945 if ((qmode
== 0) && (addquotes
!= 0)) {
1946 if (strpbrk(strtoadd
, gl_filename_quote_characters
) != NULL
) {
1947 qmode
= (strchr(strtoadd
, '"') == NULL
) ? '"' : '\'';
1948 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1950 *startp
++ = (char) qmode
;
1953 startoff
= (size_t) (startp
- buf
);
1954 amt
= strlen(strtoadd
);
1955 if ((amt
+ startoff
+ lenaftercursor
) >= bufsize
)
1956 amt
= bufsize
- (amt
+ startoff
+ lenaftercursor
);
1957 memmove(curposp
+ amt
- mlen
, curposp
, lenaftercursor
+ 1 /* NUL */);
1958 curposp
+= amt
- mlen
;
1959 memcpy(startp
, strtoadd
, amt
);
1963 /* Finish the quoting. */
1964 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1966 buf
[amt
+ startoff
] = (char) qmode
;
1969 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1971 buf
[amt
+ startoff
] = (char) gl_completion_exact_match_extra_char
;
1973 } else if ((!wasateol
) && (!isspace(*curposp
))) {
1974 /* Not a full match, but insert a
1975 * space for better readability.
1977 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1979 buf
[amt
+ startoff
] = ' ';
1981 *loc
= (int) (startoff
+ amt
);
1983 if (strtoadd1
!= NULL
)
1987 /* Don't need this any more. */
1988 for (i
=0; i
<nused
; i
++)
1989 free(gl_matchlist
[i
]);
1991 gl_matchlist
= NULL
;
1995 } /* gl_do_tab_completion */
2001 gl_tab_completion(gl_tab_completion_proc proc
)
2004 proc
= gl_local_filename_completion_proc
; /* default proc */
2005 gl_completion_proc
= proc
;
2006 } /* gl_tab_completion */
2011 #ifndef _StrFindLocalPathDelim
2013 _StrRFindLocalPathDelim(const char *src
) /* TODO: optimize */
2023 if (IsLocalPathDelim(c
))
2027 return ((char *) last
);
2028 } /* StrRFindLocalPathDelim */
2029 #endif /* Windows */
2035 gl_set_home_dir(const char *homedir
)
2039 const char *homedrive
, *homepath
;
2046 if (gl_home_dir
!= NULL
) {
2051 if (homedir
== NULL
) {
2053 homedrive
= getenv("HOMEDRIVE");
2054 homepath
= getenv("HOMEPATH");
2055 if ((homedrive
!= NULL
) && (homepath
!= NULL
)) {
2056 len
= strlen(homedrive
) + strlen(homepath
) + 1;
2057 gl_home_dir
= (char *) malloc(len
);
2058 if (gl_home_dir
!= NULL
) {
2059 strcpy(gl_home_dir
, homedrive
);
2060 strcat(gl_home_dir
, homepath
);
2066 if (GetWindowsDirectory(wdir
, sizeof(wdir
) - 1) < 1)
2067 (void) strncpy(wdir
, ".", sizeof(wdir
));
2068 else if (wdir
[1] == ':') {
2074 cp
= (char *) getlogin();
2076 cp
= (char *) getenv("LOGNAME");
2078 cp
= (char *) getenv("USER");
2084 pw
= getpwuid(getuid());
2086 return; /* hell with it */
2087 homedir
= pw
->pw_dir
;
2091 len
= strlen(homedir
) + /* NUL */ 1;
2092 gl_home_dir
= (char *) malloc(len
);
2093 if (gl_home_dir
!= NULL
) {
2094 memcpy(gl_home_dir
, homedir
, len
);
2096 } /* gl_set_home_dir */
2101 char *gl_getpass(const char *const prompt
, char *const pass
, int dsize
)
2107 memset(pass
, 0, (size_t) sizeof(dsize
));
2111 /* Display the prompt first. */
2112 if ((prompt
!= NULL
) && (prompt
[0] != '\0'))
2116 while ((c
= gl_getc()) != (-1)) {
2117 if ((c
== '\r') || (c
== '\n'))
2119 if ((c
== '\010') || (c
== '\177')) {
2127 } else if (cp
< (pass
+ dsize
)) {
2141 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
2142 ZeroMemory(pass
, (DWORD
) sizeof(dsize
));
2145 if ((prompt
!= NULL
) && (prompt
[0] != '\0'))
2150 if ((c
== '\r') || (c
== '\n'))
2152 if ((c
== '\010') || (c
== '\177')) {
2160 } else if (cp
< (pass
+ dsize
)) {
2168 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
2172 #endif /* __windows__ */
2173 #endif /* ! __unix__ */
2182 gl_local_filename_completion_proc(const char *start
, int idx
)
2184 static DIR *dir
= NULL
;
2185 static int filepfxoffset
;
2186 static size_t filepfxlen
;
2188 const char *filepfx
;
2189 struct dirent
*dent
;
2191 const char *dirtoopen
, *name
;
2198 /* shouldn't get here! */
2206 cp
= _StrRFindLocalPathDelim(start
);
2208 dirtoopen
= LOCAL_PATH_DELIM_STR
; /* root dir */
2210 } else if (cp
== NULL
) {
2214 len
= strlen(start
) + 1;
2215 dirtoopen1
= (char *) malloc(len
);
2216 if (dirtoopen1
== NULL
)
2218 memcpy(dirtoopen1
, start
, len
);
2220 dirtoopen1
[len
] = '\0';
2221 dirtoopen
= dirtoopen1
;
2222 filepfxoffset
= (int) ((cp
+ 1) - start
);
2225 if (strcmp(dirtoopen
, "~") == 0) {
2226 if (gl_home_dir
== NULL
)
2227 gl_set_home_dir(NULL
);
2228 if (gl_home_dir
== NULL
)
2230 dirtoopen
= gl_home_dir
;
2233 dir
= opendir(dirtoopen
);
2234 if (dirtoopen1
!= NULL
)
2237 filepfx
= start
+ filepfxoffset
;
2238 filepfxlen
= strlen(filepfx
);
2242 /* assumes "start" is same for each iteration. */
2243 filepfx
= start
+ filepfxoffset
;
2246 dent
= readdir(dir
);
2253 /* There was exactly one match.
2254 * In this special case, we
2255 * want to append a / instead
2258 cp
= gl_matchlist
[0];
2259 if ((cp
[0] == '~') && ((cp
[1] == '\0') || (IsLocalPathDelim(cp
[1])))) {
2260 len
= strlen(cp
+ 1) + /* NUL */ 1;
2261 len2
= strlen(gl_home_dir
);
2262 if (IsLocalPathDelim(gl_home_dir
[len2
- 1]))
2264 cp
= (char *) realloc(gl_matchlist
[0], len
+ len2
);
2266 cp
= gl_matchlist
[0];
2268 memmove(cp
+ len2
, cp
+ 1, len
);
2269 memcpy(cp
, gl_home_dir
, len2
);
2270 gl_matchlist
[0] = cp
;
2273 if ((lstat(cp
, &st
) == 0) && (S_ISDIR(st
.st_mode
)))
2274 gl_completion_exact_match_extra_char
= LOCAL_PATH_DELIM
;
2279 name
= dent
->d_name
;
2280 if ((name
[0] == '.') && ((name
[1] == '\0') || ((name
[1] == '.') && (name
[2] == '\0'))))
2281 continue; /* Skip . and .. */
2283 if ((filepfxlen
== 0) || (strncmp(name
, filepfx
, filepfxlen
) == 0)) {
2286 cp
= (char *) malloc(filepfxoffset
+ len
+ 1 /* spare */ + 1 /* NUL */);
2288 if (filepfxoffset
> 0)
2289 memcpy(cp
, start
, (size_t) filepfxoffset
);
2290 memcpy(cp
+ filepfxoffset
, name
, len
+ 1);
2297 } /* gl_local_filename_completion_proc */
2299 #endif /* __unix__ */
2308 gl_local_filename_completion_proc(const char *start
, int idx
)
2310 static HANDLE searchHandle
= NULL
;
2311 static int filepfxoffset
;
2312 static size_t filepfxlen
;
2314 WIN32_FIND_DATA ffd
;
2317 const char *filepfx
;
2318 const char *dirtoopen
, *name
;
2319 char *dirtoopen1
, *dirtoopen2
;
2323 if (searchHandle
!= NULL
) {
2324 /* shouldn't get here! */
2325 FindClose(searchHandle
);
2326 searchHandle
= NULL
;
2331 if (searchHandle
== NULL
) {
2334 cp
= _StrRFindLocalPathDelim(start
);
2336 dirtoopen
= LOCAL_PATH_DELIM_STR
; /* root dir */
2338 } else if (cp
== NULL
) {
2342 len
= strlen(start
) + 1;
2343 dirtoopen1
= (char *) malloc(len
);
2344 if (dirtoopen1
== NULL
)
2346 memcpy(dirtoopen1
, start
, len
);
2348 dirtoopen1
[len
] = '\0';
2349 dirtoopen
= dirtoopen1
;
2350 filepfxoffset
= (int) ((cp
+ 1) - start
);
2353 if (strcmp(dirtoopen
, "~") == 0) {
2354 if (gl_home_dir
== NULL
)
2355 gl_set_home_dir(NULL
);
2356 if (gl_home_dir
== NULL
)
2358 dirtoopen
= gl_home_dir
;
2361 len
= strlen(dirtoopen
);
2362 dirtoopen2
= (char *) malloc(len
+ 8);
2363 if (dirtoopen2
== NULL
) {
2364 if (dirtoopen1
!= NULL
)
2369 memcpy(dirtoopen2
, dirtoopen
, len
+ 1);
2370 if (dirtoopen2
[len
- 1] == LOCAL_PATH_DELIM
)
2371 memcpy(dirtoopen2
+ len
, "*.*", (size_t) 4);
2373 memcpy(dirtoopen2
+ len
, "\\*.*", (size_t) 5);
2375 /* "Open" the directory. */
2376 memset(&ffd
, 0, sizeof(ffd
));
2377 searchHandle
= FindFirstFile(dirtoopen2
, &ffd
);
2380 if (dirtoopen1
!= NULL
)
2383 if (searchHandle
== INVALID_HANDLE_VALUE
) {
2387 filepfx
= start
+ filepfxoffset
;
2388 filepfxlen
= strlen(filepfx
);
2390 /* assumes "start" is same for each iteration. */
2391 filepfx
= start
+ filepfxoffset
;
2397 name
= ffd
.cFileName
;
2398 if ((name
[0] == '.') && ((name
[1] == '\0') || ((name
[1] == '.') && (name
[2] == '\0'))))
2399 goto next
; /* Skip . and .. */
2401 if ((filepfxlen
== 0) || (strnicmp(name
, filepfx
, filepfxlen
) == 0)) {
2404 cp
= (char *) malloc(filepfxoffset
+ len
+ 4 /* spare */ + 1 /* NUL */);
2406 if (filepfxoffset
> 0)
2407 memcpy(cp
, start
, filepfxoffset
);
2408 memcpy(cp
+ filepfxoffset
, name
, len
+ 1);
2409 if (ffd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
2410 /* Embed file type with name. */
2411 c2
= cp
+ filepfxoffset
+ len
+ 1;
2416 c2
= cp
+ filepfxoffset
+ len
+ 1;
2425 if (!FindNextFile(searchHandle
, &ffd
)) {
2426 dwErr
= GetLastError();
2427 if (dwErr
!= ERROR_NO_MORE_FILES
) {
2428 FindClose(searchHandle
);
2429 searchHandle
= NULL
;
2434 FindClose(searchHandle
);
2435 searchHandle
= NULL
;
2438 /* There was exactly one match.
2439 * In this special case, we
2440 * want to append a \ instead
2443 cp
= gl_matchlist
[0];
2444 ch
= (char) cp
[strlen(cp
) + 2];
2445 if (ch
== (char) 'd')
2446 gl_completion_exact_match_extra_char
= LOCAL_PATH_DELIM
;
2448 if ((cp
[0] == '~') && ((cp
[1] == '\0') || (IsLocalPathDelim(cp
[1])))) {
2449 len
= strlen(cp
+ 1) + /* NUL */ 1;
2450 len2
= strlen(gl_home_dir
);
2451 if (IsLocalPathDelim(gl_home_dir
[len2
- 1]))
2453 cp
= (char *) realloc(gl_matchlist
[0], len
+ len2
+ 4);
2455 cp
= gl_matchlist
[0];
2457 memmove(cp
+ len2
, cp
+ 1, len
);
2458 memcpy(cp
, gl_home_dir
, len2
);
2459 c2
= cp
+ len
+ len2
;
2463 gl_matchlist
[0] = cp
;
2471 } /* gl_local_filename_completion_proc */
2473 #endif /* __windows__ */