2 * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
4 * Permission to use, copy, modify, and distribute this software
5 * for any purpose and without fee is hereby granted, provided
6 * that the above copyright notices appear in all copies and that both the
7 * copyright notice and this permission notice appear in supporting
8 * documentation. This software is provided "as is" without express or
11 * Thanks to the following people who have provided enhancements and fixes:
12 * Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
13 * DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
14 * Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich Betzler
18 * Note: This version has been updated by Mike Gleason <mgleason@ncftp.com>
21 static const char copyright
[] = "getline: Copyright (C) 1991, 1992, 1993, Chris Thewalt";
23 #if defined(WIN32) || defined(_WINDOWS)
25 # include <sys/types.h>
26 # include <sys/stat.h>
31 // # define strcasecmp stricmp
32 // # define strncasecmp strnicmp
33 # define sleep(a) Sleep(a * 1000)
35 # define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
36 # define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
49 # define utimbuf _utimbuf
52 # define unlink remove
55 # define LOCAL_PATH_DELIM '\\'
56 # define LOCAL_PATH_DELIM_STR "\\"
57 # define LOCAL_PATH_ALTDELIM '/'
58 # define IsLocalPathDelim(c) ((c == LOCAL_PATH_DELIM) || (c == LOCAL_PATH_ALTDELIM))
59 # define UNC_PATH_PREFIX "\\\\"
60 # define IsUNCPrefixed(s) (IsLocalPathDelim(s[0]) && IsLocalPathDelim(s[1]))
61 # define __windows__ 1
66 # if defined(AIX) || defined(_AIX)
67 # define _ALL_SOURCE 1
69 # if defined(HAVE_CONFIG_H)
73 # define HAVE_TERMIOS_H 1
74 # define HAVE_UNISTD_H 1
79 # include <sys/types.h>
80 # include <sys/time.h>
81 # include <sys/stat.h>
82 # ifdef CAN_USE_SYS_SELECT_H
83 # include <sys/select.h>
89 # ifdef HAVE_TERMIOS_H /* use HAVE_TERMIOS_H interface */
91 struct termios new_termios
, old_termios
;
92 # else /* not HAVE_TERMIOS_H */
93 # include <sys/ioctl.h>
94 # ifdef TIOCSETN /* use BSD interface */
96 struct sgttyb new_tty
, old_tty
;
99 # else /* use SYSV interface */
101 struct termio new_termio
, old_termio
;
102 # endif /* TIOCSETN */
103 # endif /* HAVE_TERMIOS_H */
104 # define LOCAL_PATH_DELIM '/'
105 # define LOCAL_PATH_DELIM_STR "/"
106 # define _StrFindLocalPathDelim(a) strchr(a, LOCAL_PATH_DELIM)
107 # define _StrRFindLocalPathDelim(a) strrchr(a, LOCAL_PATH_DELIM)
108 # define IsLocalPathDelim(c) (c == LOCAL_PATH_DELIM)
111 /********************* C library headers ********************************/
115 #ifdef HAVE_STRINGS_H
116 # include <strings.h>
122 #define _getline_c_ 1
125 static int gl_tab(char *buf
, int offset
, int *loc
, size_t bufsize
);
127 /******************** external interface *********************************/
129 gl_in_hook_proc gl_in_hook
= 0;
130 gl_out_hook_proc gl_out_hook
= 0;
131 gl_tab_hook_proc gl_tab_hook
= gl_tab
;
132 size_t gl_strlen(const char *s
) { return strlen(s
); }
133 gl_tab_completion_proc gl_completion_proc
= 0;
134 int gl_filename_quoting_desired
= -1; /* default to unspecified */
135 const char *gl_filename_quote_characters
= " \t*?<>|;&()[]$`";
136 int gl_ellipses_during_completion
= 1;
137 int gl_completion_exact_match_extra_char
;
138 char gl_buf
[GL_BUF_SIZE
]; /* input buffer */
140 /******************** internal interface *********************************/
143 static int gl_init_done
= -1; /* terminal mode flag */
144 static int gl_termw
= 80; /* actual terminal width */
145 static int gl_termh
= 24; /* actual terminal height */
146 static int gl_scroll
= 27; /* width of EOL scrolling region */
147 static int gl_width
= 0; /* net size available for input */
148 static int gl_extent
= 0; /* how far to redraw, 0 means all */
149 static int gl_overwrite
= 0; /* overwrite mode */
150 static int gl_pos
= 0, gl_cnt
= 0; /* position and size of input */
151 static char gl_killbuf
[GL_BUF_SIZE
]=""; /* killed text */
152 static const char *gl_prompt
; /* to save the prompt string */
153 static char gl_intrc
= 0; /* keyboard SIGINT char */
154 static char gl_quitc
= 0; /* keyboard SIGQUIT char */
155 // static char gl_suspc = 0; /* keyboard SIGTSTP char */
156 // static char gl_dsuspc = 0; /* delayed SIGTSTP char */
157 static int gl_search_mode
= 0; /* search mode flag */
158 static char **gl_matchlist
= 0;
159 static char *gl_home_dir
= NULL
;
160 static int gl_vi_preferred
= -1;
161 static int gl_vi_mode
= 0;
162 static int gl_result
= GL_OK
;
164 static void gl_init(void); /* prepare to edit a line */
165 static void gl_cleanup(void); /* to undo gl_init */
166 static void gl_char_init(void); /* get ready for no echo input */
167 static void gl_char_cleanup(void); /* undo gl_char_init */
168 /* returns printable prompt width */
170 static void gl_addchar(int c
); /* install specified char */
171 static void gl_del(int loc
, int); /* del, either left (-1) or cur (0) */
172 static void gl_error(const char *const buf
); /* write error msg and die */
173 static void gl_fixup(const char *prompt
, int change
, int cursor
); /* fixup state variables and screen */
174 static int gl_getc(void); /* read one char from terminal */
175 static int gl_getcx(int); /* read one char from terminal, if available before timeout */
176 static void gl_kill(int pos
); /* delete to EOL */
177 static void gl_newline(void); /* handle \n or \r */
178 static void gl_putc(int c
); /* write one char to terminal */
179 static void gl_puts(const char *const buf
); /* write a line to terminal */
180 static void gl_redraw(void); /* issue \n and redraw all */
181 static void gl_transpose(void); /* transpose two chars */
182 static void gl_yank(void); /* yank killed text */
183 static void gl_word(int direction
); /* move a word */
184 static void gl_killword(int direction
);
186 static void hist_init(void); /* initializes hist pointers */
187 static char *hist_next(void); /* return ptr to next item */
188 static char *hist_prev(void); /* return ptr to prev item */
189 static char *hist_save(char *p
); /* makes copy of a string, without NL */
191 static void search_addchar(int c
); /* increment search string */
192 static void search_term(void); /* reset with current contents */
193 static void search_back(int new_search
); /* look back for current string */
194 static void search_forw(int new_search
); /* look forw for current string */
195 static void gl_beep(void); /* try to play a system beep sound */
197 static int gl_do_tab_completion(char *buf
, int *loc
, size_t bufsize
, int tabtab
);
199 /************************ nonportable part *********************************/
206 gl_char_init(void) /* turn off input echo */
209 # ifdef HAVE_TERMIOS_H /* Use POSIX */
210 if (tcgetattr(0, &old_termios
) == 0) {
211 gl_intrc
= old_termios
.c_cc
[VINTR
];
212 gl_quitc
= old_termios
.c_cc
[VQUIT
];
214 gl_suspc
= old_termios
.c_cc
[VSUSP
];
217 gl_dsuspc
= old_termios
.c_cc
[VDSUSP
];
220 new_termios
= old_termios
;
221 new_termios
.c_iflag
&= ~(BRKINT
|ISTRIP
|IXON
|IXOFF
);
222 new_termios
.c_iflag
|= (IGNBRK
|IGNPAR
);
223 new_termios
.c_lflag
&= ~(ICANON
|ISIG
|IEXTEN
|ECHO
);
224 new_termios
.c_cc
[VMIN
] = 1;
225 new_termios
.c_cc
[VTIME
] = 0;
226 tcsetattr(0, TCSANOW
, &new_termios
);
227 # elif defined(TIOCSETN) /* BSD */
228 if (ioctl(0, TIOCGETC
, &tch
) == 0) {
229 gl_intrc
= tch
.t_intrc
;
230 gl_quitc
= tch
.t_quitc
;
232 ioctl(0, TIOCGLTC
, <ch
);
233 gl_suspc
= ltch
.t_suspc
;
234 gl_dsuspc
= ltch
.t_dsuspc
;
235 ioctl(0, TIOCGETP
, &old_tty
);
237 new_tty
.sg_flags
|= RAW
;
238 new_tty
.sg_flags
&= ~ECHO
;
239 ioctl(0, TIOCSETN
, &new_tty
);
241 if (ioctl(0, TCGETA
, &old_termio
) == 0) {
242 gl_intrc
= old_termio
.c_cc
[VINTR
];
243 gl_quitc
= old_termio
.c_cc
[VQUIT
];
245 new_termio
= old_termio
;
246 new_termio
.c_iflag
&= ~(BRKINT
|ISTRIP
|IXON
|IXOFF
);
247 new_termio
.c_iflag
|= (IGNBRK
|IGNPAR
);
248 new_termio
.c_lflag
&= ~(ICANON
|ISIG
|ECHO
);
249 new_termio
.c_cc
[VMIN
] = 1;
250 new_termio
.c_cc
[VTIME
] = 0;
251 ioctl(0, TCSETA
, &new_termio
);
253 #endif /* __unix__ */
257 gl_char_cleanup(void) /* undo effects of gl_char_init */
260 # ifdef HAVE_TERMIOS_H
261 tcsetattr(0, TCSANOW
, &old_termios
);
262 # elif defined(TIOCSETN) /* BSD */
263 ioctl(0, TIOCSETN
, &old_tty
);
265 ioctl(0, TCSETA
, &old_termio
);
267 #endif /* __unix__ */
276 } /* gl_get_result */
281 #if defined(MSDOS) || defined(__windows__)
287 #define K_DELETE 0x53
288 #define K_INSERT 0x52
299 c
= 16; /* up -> ^P */
303 c
= 14; /* down -> ^N */
306 c
= 2; /* left -> ^B */
309 c
= 6; /* right -> ^F */
312 c
= 5; /* end -> ^E */
315 c
= 1; /* home -> ^A */
318 c
= 15; /* insert -> ^O */
321 c
= 4; /* del -> ^D */
324 c
= 0; /* make it garbage */
328 #endif /* defined(MSDOS) || defined(__windows__) */
332 /* get a character without echoing it to screen */
341 while ((c
= (int) read(0, &ch
, 1)) == -1) {
347 #endif /* __unix__ */
349 c
= _bios_keybrd(_NKEYBRD_READ
);
350 if ((c
& 0377) == 224) {
351 c
= pc_keymap((c
>> 8) & 0377);
358 if ((c
== 0) || (c
== 0xE0)) {
362 } else if (c
== '\r') {
363 /* Note: we only get \r from the console,
364 * and not a matching \n.
378 /* Get a character without echoing it to screen, timing out
379 * after tlen tenths of a second.
389 FD_SET(0, &ss
); /* set STDIN_FILENO */
390 tv
.tv_sec
= tlen
/ 10;
391 tv
.tv_usec
= (tlen
% 10) * 100000L;
392 result
= select(1, &ss
, NULL
, NULL
, &tv
);
396 } else if (result
== 0) {
399 } else if (errno
!= EINTR
) {
405 c
= (int) read(0, &ch
, 1);
415 #endif /* __unix__ */
428 tlen
-= 2; /* Adjust for 200ms overhead */
431 for (i
=0; i
<tlen
; i
++) {
434 if ((c
== 0) || (c
== 0xE0)) {
441 (void) SleepEx((DWORD
) (tlen
* 100), FALSE
);
446 #endif /* __windows__ */
454 char ch
= (char) (unsigned char) c
;
459 write(1, &ch
, 1); /* RAW mode needs '\r', does not hurt */
463 /******************** fairly portable part *********************************/
466 gl_puts(const char *const buf
)
471 len
= (int) strlen(buf
);
477 gl_error(const char *const buf
)
479 int len
= (int) strlen(buf
);
488 /* set up variables and terminal */
493 if (gl_init_done
< 0) { /* -1 only on startup */
494 cp
= (const char *) getenv("COLUMNS");
500 cp
= (const char *) getenv("ROWS");
508 if (_isatty(0) == 0 || _isatty(1) == 0)
509 gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
516 /* undo effects of gl_init, as necessary */
518 if (gl_init_done
> 0)
523 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
529 gl_check_inputrc_for_vi(void)
534 /* If the user has a ~/.inputrc file,
535 * check it to see if it has a line like
536 * "set editing-mode vi". If it does,
537 * we know that the user wants vi
538 * emulation rather than emacs. If the
539 * file doesn't exist, it's no big
540 * deal since we can also check the
541 * $EDITOR environment variable.
543 gl_set_home_dir(NULL
);
544 if (gl_home_dir
== NULL
)
548 snprintf(path
, sizeof(path
), "%s/%s", gl_home_dir
, ".inputrc");
550 if (sizeof(path
) >= (strlen(gl_home_dir
) + strlen("/.inputrc")))
553 sprintf(path
, "%s%s", gl_home_dir
, "/.inputrc");
558 #if defined(__windows__) || defined(MSDOS)
568 while (fgets(path
, sizeof(path
) - 1, fp
) != NULL
) {
569 if ((strstr(path
, "editing-mode") != NULL
) && (strstr(path
, "vi") != NULL
)) {
576 } /* gl_check_inputrc_for_vi */
589 gl_error("\n*** Error: minimum screen width is 21\n");
601 gl_error("\n*** Error: minimum screen height is 10\n");
609 getline(char *prompt
)
611 int c
, loc
, tmp
, lastch
;
614 char vi_countbuf
[32];
621 /* We'll change the result code only if something happens later. */
624 /* Even if it appears that "vi" is preferred, we
625 * don't start in gl_vi_mode. They need to hit
626 * ESC to go into vi command mode.
631 if (gl_vi_preferred
< 0) {
633 cp
= (char *) getenv("EDITOR");
635 gl_vi_preferred
= (strstr(cp
, "vi") != NULL
);
636 if (gl_vi_preferred
== 0)
637 gl_check_inputrc_for_vi();
641 gl_prompt
= (prompt
)? prompt
: "";
645 gl_fixup(gl_prompt
, -2, GL_BUF_SIZE
);
649 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
652 while ((c
= gl_getc()) != (-1)) {
653 gl_extent
= 0; /* reset to full extent */
654 /* Note: \n may or may not be considered printable */
655 if ((c
!= '\t') && ((isprint(c
) != 0) || ((c
& 0x80) != 0))) {
656 if (gl_vi_mode
> 0) {
657 /* "vi" emulation -- far from perfect,
658 * but reasonably functional.
661 for (count
= 0; ; ) {
663 if (vi_countbuf
[sizeof(vi_countbuf
) - 2] == '\0')
664 vi_countbuf
[strlen(vi_countbuf
)] = (char) c
;
665 } else if (vi_countbuf
[0] != '\0') {
666 vi_count
= atoi(vi_countbuf
);
667 memset(vi_countbuf
, 0, sizeof(vi_countbuf
));
683 gl_fixup(gl_prompt
, -1, gl_pos
-1);
687 gl_fixup(gl_prompt
, -1, gl_pos
-1);
691 case 'l': /* right */
695 gl_fixup(gl_prompt
, -1, gl_pos
+1);
699 strcpy(gl_buf
, hist_prev());
702 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
705 strcpy(gl_buf
, hist_next());
708 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
711 if (vi_delete
== 1) {
720 case '^': /* start of line */
723 gl_fixup(gl_prompt
, -1, 0);
724 for (c
= 0; c
< vi_count
; c
++) {
731 gl_fixup(gl_prompt
, -1, 0);
734 case '$': /* end of line */
738 loc
= (int) strlen(gl_buf
);
741 gl_fixup(gl_prompt
, -1, loc
);
744 case 'p': /* paste after */
745 gl_fixup(gl_prompt
, -1, gl_pos
+1);
748 case 'P': /* paste before */
751 case 'r': /* replace character */
752 gl_buf
[gl_pos
] = (char) gl_getc();
753 gl_fixup(gl_prompt
, gl_pos
, gl_pos
);
770 gl_fixup(gl_prompt
, -1, gl_pos
+1);
775 if (count
>= vi_count
)
782 } else if (gl_search_mode
) {
788 if (gl_search_mode
) {
789 if (c
== '\033' || c
== '\016' || c
== '\020') {
791 c
= 0; /* ignore the character */
792 } else if (c
== '\010' || c
== '\177') {
793 search_addchar(-1); /* unwind search string */
795 } else if (c
!= '\022' && c
!= '\023') {
796 search_term(); /* terminate and handle char */
800 case '\n': case '\r': /* newline */
804 case '\001': gl_fixup(gl_prompt
, -1, 0); /* ^A */
806 case '\002': gl_fixup(gl_prompt
, -1, gl_pos
-1); /* ^B */
808 case '\004': /* ^D */
819 case '\005': gl_fixup(gl_prompt
, -1, gl_cnt
); /* ^E */
821 case '\006': gl_fixup(gl_prompt
, -1, gl_pos
+1); /* ^F */
823 case '\010': case '\177': gl_del(-1, 0); /* ^H and DEL */
826 if (gl_completion_proc
) {
828 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
829 loc
= gl_do_tab_completion(gl_buf
, &tmp
, sizeof(gl_buf
), (lastch
== '\t'));
830 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
831 if (loc
>= 0 || tmp
!= gl_pos
)
832 gl_fixup(gl_prompt
, /* loc */ -2, tmp
);
833 if (lastch
== '\t') {
837 } else if (gl_tab_hook
) {
839 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
840 loc
= gl_tab_hook(gl_buf
, (int) gl_strlen(gl_prompt
), &tmp
, sizeof(gl_buf
));
841 gl_buf
[sizeof(gl_buf
) - 1] = '\0';
842 if (loc
>= 0 || tmp
!= gl_pos
)
843 gl_fixup(gl_prompt
, loc
, tmp
);
846 case '\013': gl_kill(gl_pos
); /* ^K */
848 case '\014': gl_redraw(); /* ^L */
850 case '\016': /* ^N */
851 strcpy(gl_buf
, hist_next());
854 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
856 case '\017': gl_overwrite
= !gl_overwrite
; /* ^O */
858 case '\020': /* ^P */
859 strcpy(gl_buf
, hist_prev());
862 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
864 case '\022': search_back(1); /* ^R */
866 case '\023': search_forw(1); /* ^S */
868 case '\024': gl_transpose(); /* ^T */
870 case '\025': gl_kill(0); /* ^U */
872 case '\027': gl_killword(-1); /* ^W */
874 case '\031': gl_yank(); /* ^Y */
876 case '\033': /* ansi arrow keys */
878 if ((c
== '[') || (c
== 'O')) {
880 switch(c
= gl_getc()) {
882 strcpy(gl_buf
, hist_prev());
885 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
888 strcpy(gl_buf
, hist_next());
891 gl_fixup(gl_prompt
, 0, GL_BUF_SIZE
);
894 gl_fixup(gl_prompt
, -1, gl_pos
+1); /* right */
897 gl_fixup(gl_prompt
, -1, gl_pos
-1); /* left */
902 default: gl_beep(); /* who knows */
905 } else if ((gl_vi_preferred
== 0) && ((c
== 'f') || (c
== 'F'))) {
907 } else if ((gl_vi_preferred
== 0) && ((c
== 'b') || (c
== 'B'))) {
909 } else if (c
!= (-1)) {
910 /* enter vi command mode */
911 #if defined(__windows__) || defined(MSDOS)
912 if (gl_vi_preferred
== 0) {
913 /* On Windows, ESC acts like a line kill,
914 * so don't use vi mode unless they prefer
920 if (gl_vi_mode
== 0) {
924 memset(vi_countbuf
, 0, sizeof(vi_countbuf
));
926 gl_fixup(gl_prompt
, -2, gl_pos
-1); /* left 1 char */
927 /* Don't bother if the line is empty and we don't
928 * know for sure if the user wants vi mode.
930 if ((gl_cnt
> 0) || (gl_vi_preferred
== 1)) {
931 /* We still have to use the char read! */
940 default: /* check for a terminal signal */
941 if (c
> 0) { /* ignore 0 (reset above) */
943 gl_result
= GL_INTERRUPT
;
955 gl_result
= GL_INTERRUPT
;
967 if (c
== gl_suspc
|| c
== gl_dsuspc
) {
969 gl_result
= GL_INTERRUPT
;
979 #endif /* __unix__ */
997 /* adds the character c to the input buffer at current location */
1001 if (gl_cnt
>= GL_BUF_SIZE
- 1)
1002 gl_error("\n*** Error: getline(): input buffer overflow\n");
1003 if (gl_overwrite
== 0 || gl_pos
== gl_cnt
) {
1004 for (i
=gl_cnt
; i
>= gl_pos
; i
--)
1005 gl_buf
[i
+1] = gl_buf
[i
];
1006 gl_buf
[gl_pos
] = (char) c
;
1007 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+1);
1009 gl_buf
[gl_pos
] = (char) c
;
1011 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+1);
1017 /* adds the kill buffer to the input buffer at current location */
1021 len
= (int) strlen(gl_killbuf
);
1023 if (gl_overwrite
== 0) {
1024 if (gl_cnt
+ len
>= GL_BUF_SIZE
- 1)
1025 gl_error("\n*** Error: getline(): input buffer overflow\n");
1026 for (i
=gl_cnt
; i
>= gl_pos
; i
--)
1027 gl_buf
[i
+len
] = gl_buf
[i
];
1028 for (i
=0; i
< len
; i
++)
1029 gl_buf
[gl_pos
+i
] = gl_killbuf
[i
];
1030 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+len
);
1032 if (gl_pos
+ len
> gl_cnt
) {
1033 if (gl_pos
+ len
>= GL_BUF_SIZE
- 1)
1034 gl_error("\n*** Error: getline(): input buffer overflow\n");
1035 gl_buf
[gl_pos
+ len
] = 0;
1037 for (i
=0; i
< len
; i
++)
1038 gl_buf
[gl_pos
+i
] = gl_killbuf
[i
];
1040 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+len
);
1048 /* switch character under cursor and to left of cursor */
1052 if (gl_pos
> 0 && gl_cnt
> gl_pos
) {
1053 c
= gl_buf
[gl_pos
-1];
1054 gl_buf
[gl_pos
-1] = gl_buf
[gl_pos
];
1055 gl_buf
[gl_pos
] = (char) c
;
1057 gl_fixup(gl_prompt
, gl_pos
-1, gl_pos
);
1065 * Cleans up entire line before returning to caller. A \n is appended.
1066 * If line longer than screen, we redraw starting at beginning
1069 int change
= gl_cnt
;
1071 int loc
= gl_width
- 5; /* shifts line back to start position */
1073 if (gl_cnt
>= GL_BUF_SIZE
- 1)
1074 gl_error("\n*** Error: getline(): input buffer overflow\n");
1076 change
= gl_out_hook(gl_buf
);
1077 len
= (int) strlen(gl_buf
);
1081 gl_fixup(gl_prompt
, change
, loc
); /* must do this before appending \n */
1083 gl_buf
[len
+1] = '\0';
1088 gl_del(int loc
, int killsave
)
1091 * Delete a character. The loc variable can be:
1092 * -1 : delete character to left of cursor
1093 * 0 : delete character under cursor
1098 if ((loc
== -1 && gl_pos
> 0) || (loc
== 0 && gl_pos
< gl_cnt
)) {
1099 for (j
=0, i
=gl_pos
+loc
; i
< gl_cnt
; i
++) {
1100 if ((j
== 0) && (killsave
!= 0) && (gl_vi_mode
!= 0)) {
1101 gl_killbuf
[0] = gl_buf
[i
];
1102 gl_killbuf
[1] = '\0';
1105 gl_buf
[i
] = gl_buf
[i
+1];
1107 gl_fixup(gl_prompt
, gl_pos
+loc
, gl_pos
+loc
);
1115 /* delete from pos to the end of line */
1118 strcpy(gl_killbuf
, gl_buf
+ pos
);
1120 gl_fixup(gl_prompt
, pos
, pos
);
1126 gl_killword(int direction
)
1129 int startpos
= gl_pos
;
1133 if (direction
> 0) { /* forward */
1134 while (!isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1136 while (isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1138 } else { /* backward */
1141 while (isspace(gl_buf
[pos
]) && pos
> 0)
1143 while (!isspace(gl_buf
[pos
]) && pos
> 0)
1145 if (pos
< gl_cnt
&& isspace(gl_buf
[pos
])) /* move onto word */
1148 if (pos
< startpos
) {
1153 memcpy(gl_killbuf
, gl_buf
+ startpos
, (size_t) (pos
- startpos
));
1154 gl_killbuf
[pos
- startpos
] = '\0';
1155 if (isspace(gl_killbuf
[pos
- startpos
- 1]))
1156 gl_killbuf
[pos
- startpos
- 1] = '\0';
1157 gl_fixup(gl_prompt
, -1, startpos
);
1158 for (i
=0, tmp
=pos
- startpos
; i
<tmp
; i
++)
1163 gl_word(int direction
)
1165 /* move forward or backword one word */
1169 if (direction
> 0) { /* forward */
1170 while (!isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1172 while (isspace(gl_buf
[pos
]) && pos
< gl_cnt
)
1174 } else { /* backword */
1177 while (isspace(gl_buf
[pos
]) && pos
> 0)
1179 while (!isspace(gl_buf
[pos
]) && pos
> 0)
1181 if (pos
< gl_cnt
&& isspace(gl_buf
[pos
])) /* move onto word */
1184 gl_fixup(gl_prompt
, -1, pos
);
1189 /* emit a newline, reset and redraw prompt and current input line */
1191 if (gl_init_done
> 0) {
1193 gl_fixup(gl_prompt
, -2, gl_pos
);
1198 gl_fixup(const char *prompt
, int change
, int cursor
)
1202 * This function is used both for redrawing when input changes or for
1203 * moving within the input line. The parameters are:
1204 * prompt: compared to last_prompt[] for changes;
1205 * change : the index of the start of changes in the input buffer,
1206 * with -1 indicating no changes, -2 indicating we're on
1207 * a new line, redraw everything.
1208 * cursor : the desired location of the cursor after the call.
1209 * A value of GL_BUF_SIZE can be used to indicate the cursor should
1210 * move just past the end of the input line.
1213 static int gl_shift
; /* index of first on screen character */
1214 static int off_right
; /* true if more text right of screen */
1215 static int off_left
; /* true if more text left of screen */
1216 static char last_prompt
[80] = "";
1217 int left
= 0, right
= -1; /* bounds for redraw */
1218 int pad
; /* how much to erase at end of line */
1219 int backup
; /* how far to backup before fixing */
1220 int new_shift
; /* value of shift based on cursor */
1221 int extra
; /* adjusts when shift (scroll) happens */
1223 int new_right
= -1; /* alternate right bound, using gl_extent */
1226 if (change
== -2) { /* reset */
1227 gl_pos
= gl_cnt
= gl_shift
= off_right
= off_left
= 0;
1230 strcpy(last_prompt
, prompt
);
1232 gl_width
= gl_termw
- (int) gl_strlen(prompt
);
1233 } else if (strcmp(prompt
, last_prompt
) != 0) {
1234 l1
= (int) gl_strlen(last_prompt
);
1235 l2
= (int) gl_strlen(prompt
);
1236 gl_cnt
= gl_cnt
+ l1
- l2
;
1237 strcpy(last_prompt
, prompt
);
1241 gl_width
= gl_termw
- l2
;
1244 pad
= (off_right
)? gl_width
- 1 : gl_cnt
- gl_shift
; /* old length */
1245 backup
= gl_pos
- gl_shift
;
1247 gl_cnt
= (int) strlen(gl_buf
);
1248 if (change
> gl_cnt
)
1251 if (cursor
> gl_cnt
) {
1252 if (cursor
!= GL_BUF_SIZE
) { /* GL_BUF_SIZE means end of line */
1253 if (gl_ellipses_during_completion
== 0) {
1263 if (off_right
|| (off_left
&& cursor
< gl_shift
+ gl_width
- gl_scroll
/ 2))
1264 extra
= 2; /* shift the scrolling boundary */
1267 new_shift
= cursor
+ extra
+ gl_scroll
- gl_width
;
1268 if (new_shift
> 0) {
1269 new_shift
/= gl_scroll
;
1270 new_shift
*= gl_scroll
;
1273 if (new_shift
!= gl_shift
) { /* scroll occurs */
1274 gl_shift
= new_shift
;
1275 off_left
= (gl_shift
)? 1 : 0;
1276 off_right
= (gl_cnt
> gl_shift
+ gl_width
- 1)? 1 : 0;
1278 new_right
= right
= (off_right
)? gl_shift
+ gl_width
- 2 : gl_cnt
;
1279 } else if (change
>= 0) { /* no scroll, but text changed */
1280 if (change
< gl_shift
+ off_left
) {
1284 backup
= gl_pos
- change
;
1286 off_right
= (gl_cnt
> gl_shift
+ gl_width
- 1)? 1 : 0;
1287 right
= (off_right
)? gl_shift
+ gl_width
- 2 : gl_cnt
;
1288 new_right
= (gl_extent
&& (right
> left
+ gl_extent
))?
1289 left
+ gl_extent
: right
;
1291 pad
-= (off_right
)? gl_width
- 1 : gl_cnt
- gl_shift
;
1292 pad
= (pad
< 0)? 0 : pad
;
1293 if (left
<= right
) { /* clean up screen */
1294 for (i
=0; i
< backup
; i
++)
1296 if (left
== gl_shift
&& off_left
) {
1300 for (i
=left
; i
< new_right
; i
++)
1303 if (off_right
&& new_right
== right
) {
1307 for (i
=0; i
< pad
; i
++) /* erase remains of prev line */
1312 i
= gl_pos
- cursor
; /* move to final cursor location */
1317 for (i
=gl_pos
; i
< cursor
; i
++)
1324 gl_tab(char *buf
, int offset
, int *loc
, size_t bufsize
)
1325 /* default tab handler, acts like tabstops every 8 cols */
1329 len
= (int) strlen(buf
);
1330 count
= 8 - (offset
+ *loc
) % 8;
1331 for (i
=len
; i
>= *loc
; i
--)
1332 if (i
+count
< (int) bufsize
)
1333 buf
[i
+count
] = buf
[i
];
1334 for (i
=0; i
< count
; i
++)
1335 if (*loc
+i
< (int) bufsize
)
1342 /******************* History stuff **************************************/
1345 #define HIST_SIZE 100
1348 static int hist_pos
= 0, hist_last
= 0;
1349 static char *hist_buf
[HIST_SIZE
];
1350 static char hist_empty_elem
[2] = "";
1357 hist_buf
[0] = hist_empty_elem
;
1358 for (i
=1; i
< HIST_SIZE
; i
++)
1359 hist_buf
[i
] = (char *)0;
1363 gl_histadd(char *buf
)
1365 static char *prev
= 0;
1369 /* in case we call gl_histadd() before we call getline() */
1370 if (gl_init_done
< 0) { /* -1 only on startup */
1374 while (*p
== ' ' || *p
== '\t' || *p
== '\n')
1377 len
= (int) strlen(buf
);
1378 if (strchr(p
, '\n')) /* previously line already has NL stripped */
1380 if ((prev
== 0) || ((int) strlen(prev
) != len
) ||
1381 strncmp(prev
, buf
, (size_t) len
) != 0) {
1382 hist_buf
[hist_last
] = hist_save(buf
);
1383 prev
= hist_buf
[hist_last
];
1384 hist_last
= (hist_last
+ 1) % HIST_SIZE
;
1385 if (hist_buf
[hist_last
] && *hist_buf
[hist_last
]) {
1386 free(hist_buf
[hist_last
]);
1388 hist_buf
[hist_last
] = hist_empty_elem
;
1391 hist_pos
= hist_last
;
1396 /* loads previous hist entry into input buffer, sticks on first */
1399 int next
= (hist_pos
- 1 + HIST_SIZE
) % HIST_SIZE
;
1401 if (hist_buf
[hist_pos
] != 0 && next
!= hist_last
) {
1403 p
= hist_buf
[hist_pos
];
1406 p
= hist_empty_elem
;
1414 /* loads next hist entry into input buffer, clears on last */
1418 if (hist_pos
!= hist_last
) {
1419 hist_pos
= (hist_pos
+1) % HIST_SIZE
;
1420 p
= hist_buf
[hist_pos
];
1423 p
= hist_empty_elem
;
1432 /* makes a copy of the string */
1435 size_t len
= strlen(p
);
1436 char *nl
= strpbrk(p
, "\n\r");
1439 if ((s
= (char *) malloc(len
)) != 0) {
1440 strncpy(s
, p
, len
-1);
1444 if ((s
= (char *) malloc(len
+1)) != 0) {
1449 gl_error("\n*** Error: hist_save() failed on malloc\n");
1457 gl_histsavefile(const char *const path
)
1464 #if defined(__windows__) || defined(MSDOS)
1471 for (i
=2; i
<HIST_SIZE
; i
++) {
1472 j
= (hist_pos
+i
) % HIST_SIZE
;
1474 if ((p
== NULL
) || (*p
== '\0'))
1476 fprintf(fp
, "%s\n", p
);
1480 } /* gl_histsavefile */
1486 gl_histloadfile(const char *const path
)
1492 #if defined(__windows__) || defined(MSDOS)
1499 memset(line
, 0, sizeof(line
));
1500 while (fgets(line
, sizeof(line
) - 2, fp
) != NULL
) {
1505 } /* gl_histloadfile */
1510 /******************* Search stuff **************************************/
1512 static char search_prompt
[101]; /* prompt includes search string */
1513 static char search_string
[100];
1514 static int search_pos
= 0; /* current location in search_string */
1515 static int search_forw_flg
= 0; /* search direction flag */
1516 static int search_last
= 0; /* last match found */
1519 search_update(int c
)
1523 search_string
[0] = 0;
1524 search_prompt
[0] = '?';
1525 search_prompt
[1] = ' ';
1526 search_prompt
[2] = 0;
1528 search_string
[search_pos
] = (char) c
;
1529 search_string
[search_pos
+1] = (char) 0;
1530 search_prompt
[search_pos
] = (char) c
;
1531 search_prompt
[search_pos
+1] = (char) '?';
1532 search_prompt
[search_pos
+2] = (char) ' ';
1533 search_prompt
[search_pos
+3] = (char) 0;
1536 if (search_pos
> 0) {
1538 search_string
[search_pos
] = (char) 0;
1539 search_prompt
[search_pos
] = (char) '?';
1540 search_prompt
[search_pos
+1] = (char) ' ';
1541 search_prompt
[search_pos
+2] = (char) 0;
1544 hist_pos
= hist_last
;
1550 search_addchar(int c
)
1556 if (search_pos
> 0) {
1557 hist_pos
= search_last
;
1560 hist_pos
= hist_last
;
1562 strcpy(gl_buf
, hist_buf
[hist_pos
]);
1564 if ((loc
= strstr(gl_buf
, search_string
)) != 0) {
1565 gl_fixup(search_prompt
, 0, (int) (loc
- gl_buf
));
1566 } else if (search_pos
> 0) {
1567 if (search_forw_flg
) {
1573 gl_fixup(search_prompt
, 0, 0);
1581 if (gl_buf
[0] == 0) /* not found, reset hist list */
1582 hist_pos
= hist_last
;
1585 gl_fixup(gl_prompt
, 0, gl_pos
);
1589 search_back(int new_search
)
1594 search_forw_flg
= 0;
1595 if (gl_search_mode
== 0) {
1596 search_last
= hist_pos
= hist_last
;
1600 gl_fixup(search_prompt
, 0, 0);
1601 } else if (search_pos
> 0) {
1604 if (*p
== 0) { /* not found, done looking */
1606 gl_fixup(search_prompt
, 0, 0);
1608 } else if ((loc
= strstr(p
, search_string
)) != 0) {
1610 gl_fixup(search_prompt
, 0, (int) (loc
- p
));
1612 search_last
= hist_pos
;
1623 search_forw(int new_search
)
1628 search_forw_flg
= 1;
1629 if (gl_search_mode
== 0) {
1630 search_last
= hist_pos
= hist_last
;
1634 gl_fixup(search_prompt
, 0, 0);
1635 } else if (search_pos
> 0) {
1638 if (*p
== 0) { /* not found, done looking */
1640 gl_fixup(search_prompt
, 0, 0);
1642 } else if ((loc
= strstr(p
, search_string
)) != 0) {
1644 gl_fixup(search_prompt
, 0, (int) (loc
- p
));
1646 search_last
= hist_pos
;
1669 gl_display_matches_sort_proc(const void *a
, const void *b
)
1672 * ((const char **) a
),
1673 * ((const char **) b
)
1675 } /* gl_display_matches_sort_proc */
1680 gl_display_matches(int nused
)
1684 size_t ilen
, imaxlen
;
1687 int nmax
, ncol
, colw
, nrow
;
1688 char *cp1
, *cp2
, *lim
, *itemp
;
1693 gl_puts(" (no matches)");
1696 qsort(gl_matchlist
, (size_t) nused
, sizeof(char *), gl_display_matches_sort_proc
);
1698 /* Find the greatest amount that matches. */
1699 for (glen
= 0; ; glen
++) {
1701 for (i
=1; i
<nused
; i
++) {
1702 if (gl_matchlist
[0][glen
] != gl_matchlist
[i
][glen
]) {
1712 if (!isalnum(gl_matchlist
[0][glen
- 1]))
1718 imaxlen
= strlen(gl_matchlist
[0]);
1719 for (i
=1; i
<nused
; i
++) {
1720 ilen
= strlen(gl_matchlist
[i
]);
1725 /* Subtract amount we'll skip for each item. */
1728 ncol
= (gl_termw
- 8) / ((int) imaxlen
+ 2);
1732 colw
= (gl_termw
- 8) / ncol
;
1734 if ((nused
% ncol
) != 0)
1737 if (nrow
> (gl_termh
- 4)) {
1738 nrow
= gl_termh
- 4;
1742 for (i
=0; i
<(int) sizeof(buf2
); i
++)
1745 for (j
=0; j
<nrow
; j
++) {
1746 (void) memcpy(buf
, buf2
, sizeof(buf
));
1747 for (i
=0, k
=j
, l
=4; i
<ncol
; i
++, k
+= nrow
, l
+= colw
) {
1750 itemp
= gl_matchlist
[k
] + glen
;
1752 lim
= cp1
+ (int) strlen(itemp
);
1753 if (lim
> (buf
+ sizeof(buf
) - 1))
1759 for (cp1
= buf
+ sizeof(buf
); *--cp1
== ' '; )
1768 (void) sprintf(buf
, " ... %d others omitted ...", (nused
- nmax
));
1773 gl_fixup(gl_prompt
, -2, GL_BUF_SIZE
);
1774 } /* gl_display_matches */
1780 gl_do_tab_completion(char *buf
, int *loc
, size_t bufsize
, int tabtab
)
1783 size_t startoff
, amt
;
1787 char *lastspacestart
;
1789 int ntoalloc
, nused
, nprocused
, nalloced
, i
;
1790 char **newgl_matchlist
;
1791 char *strtoadd
, *strtoadd1
;
1793 size_t llen
, mlen
, glen
;
1796 size_t lenaftercursor
;
1799 char ellipsessave
[4];
1801 /* Zero out the rest of the buffer, so we can move stuff around
1802 * and know we'll still be NUL-terminated.
1805 memset(buf
+ llen
, 0, bufsize
- llen
);
1806 bufsize
-= 4; /* leave room for a NUL, space, and two quotes. */
1807 curposp
= buf
+ *loc
;
1808 wasateol
= (*curposp
== '\0');
1809 lenaftercursor
= llen
- (curposp
- buf
);
1810 if (gl_ellipses_during_completion
!= 0) {
1811 memcpy(ellipsessave
, curposp
, (size_t) 4);
1812 memcpy(curposp
, "... ", (size_t) 4);
1813 gl_fixup(gl_prompt
, gl_pos
, gl_pos
+ 3);
1814 memcpy(curposp
, ellipsessave
, (size_t) 4);
1819 lastspacestart
= NULL
;
1823 while (cp
< curposp
) {
1827 if ((c
== '"') || (c
== '\'')) {
1829 /* closing quote; end it. */
1832 } else if (qmode
!= 0) {
1833 /* just treat it as a regular char. */
1835 /* start new quote group. */
1839 } else if ((isspace(c
)) && (qmode
== 0)) {
1840 /* found a non-quoted space. */
1841 lastspacestart
= cp
- 1;
1848 startp
= qstart
+ 1;
1849 else if (lastspacestart
!= NULL
)
1850 startp
= lastspacestart
+ 1;
1855 mlen
= (curposp
- cp
);
1857 matchpfx
= (char *) malloc(mlen
+ 1);
1858 memcpy(matchpfx
, cp
, mlen
);
1859 matchpfx
[mlen
] = '\0';
1861 #define GL_COMPLETE_VECTOR_BLOCK_SIZE 64
1864 ntoalloc
= GL_COMPLETE_VECTOR_BLOCK_SIZE
;
1865 newgl_matchlist
= (char **) malloc((size_t) (sizeof(char *) * (ntoalloc
+ 1)));
1866 if (newgl_matchlist
== NULL
) {
1871 gl_matchlist
= newgl_matchlist
;
1872 nalloced
= ntoalloc
;
1873 for (i
=nused
; i
<=nalloced
; i
++)
1874 gl_matchlist
[i
] = NULL
;
1876 gl_completion_exact_match_extra_char
= ' ';
1877 for (nprocused
= 0;; nprocused
++) {
1878 if (nused
== nalloced
) {
1879 ntoalloc
+= GL_COMPLETE_VECTOR_BLOCK_SIZE
;
1880 newgl_matchlist
= (char **) realloc((char *) gl_matchlist
, (size_t) (sizeof(char *) * (ntoalloc
+ 1)));
1881 if (newgl_matchlist
== NULL
) {
1882 /* not enough memory to expand list -- abort */
1883 for (i
=0; i
<nused
; i
++)
1884 free(gl_matchlist
[i
]);
1886 gl_matchlist
= NULL
;
1891 gl_matchlist
= newgl_matchlist
;
1892 nalloced
= ntoalloc
;
1893 for (i
=nused
; i
<=nalloced
; i
++)
1894 gl_matchlist
[i
] = NULL
;
1896 cp
= gl_completion_proc(matchpfx
, nprocused
);
1899 if ((cp
[0] == '.') && ((cp
[1] == '\0') || ((cp
[1] == '.') && (cp
[2] == '\0'))))
1900 continue; /* Skip . and .. */
1901 gl_matchlist
[nused
++] = cp
;
1904 if (gl_ellipses_during_completion
!= 0) {
1905 gl_fixup(gl_prompt
, gl_pos
, gl_pos
);
1909 /* We now have an array strings, whose last element is NULL. */
1914 addquotes
= (gl_filename_quoting_desired
> 0) || ((gl_filename_quoting_desired
< 0) && (gl_completion_proc
== gl_local_filename_completion_proc
));
1917 /* Exactly one match. */
1918 strtoadd
= gl_matchlist
[0];
1919 } else if (tabtab
!= 0) {
1920 /* TAB-TAB: print all matches */
1921 gl_display_matches(nused
);
1922 } else if ((nused
> 1) && (mlen
> 0)) {
1923 /* Find the greatest amount that matches. */
1924 for (glen
= strlen(matchpfx
); ; glen
++) {
1926 for (i
=1; i
<nused
; i
++) {
1927 if (gl_matchlist
[0][glen
] != gl_matchlist
[i
][glen
]) {
1935 strtoadd1
= (char *) malloc(glen
+ 1);
1936 if (strtoadd1
!= NULL
) {
1937 memcpy(strtoadd1
, gl_matchlist
[0], glen
);
1938 strtoadd1
[glen
] = '\0';
1939 strtoadd
= strtoadd1
;
1943 if (strtoadd
!= NULL
) {
1944 if ((qmode
== 0) && (addquotes
!= 0)) {
1945 if (strpbrk(strtoadd
, gl_filename_quote_characters
) != NULL
) {
1946 qmode
= (strchr(strtoadd
, '"') == NULL
) ? '"' : '\'';
1947 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1949 *startp
++ = (char) qmode
;
1952 startoff
= (size_t) (startp
- buf
);
1953 amt
= strlen(strtoadd
);
1954 if ((amt
+ startoff
+ lenaftercursor
) >= bufsize
)
1955 amt
= bufsize
- (amt
+ startoff
+ lenaftercursor
);
1956 memmove(curposp
+ amt
- mlen
, curposp
, lenaftercursor
+ 1 /* NUL */);
1957 curposp
+= amt
- mlen
;
1958 memcpy(startp
, strtoadd
, amt
);
1962 /* Finish the quoting. */
1963 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1965 buf
[amt
+ startoff
] = (char) qmode
;
1968 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1970 buf
[amt
+ startoff
] = (char) gl_completion_exact_match_extra_char
;
1972 } else if ((!wasateol
) && (!isspace(*curposp
))) {
1973 /* Not a full match, but insert a
1974 * space for better readability.
1976 memmove(curposp
+ 1, curposp
, lenaftercursor
+ 1 /* NUL */);
1978 buf
[amt
+ startoff
] = ' ';
1980 *loc
= (int) (startoff
+ amt
);
1982 if (strtoadd1
!= NULL
)
1986 /* Don't need this any more. */
1987 for (i
=0; i
<nused
; i
++)
1988 free(gl_matchlist
[i
]);
1990 gl_matchlist
= NULL
;
1994 } /* gl_do_tab_completion */
2000 gl_tab_completion(gl_tab_completion_proc proc
)
2003 proc
= gl_local_filename_completion_proc
; /* default proc */
2004 gl_completion_proc
= proc
;
2005 } /* gl_tab_completion */
2010 #ifndef _StrFindLocalPathDelim
2012 _StrRFindLocalPathDelim(const char *src
) /* TODO: optimize */
2022 if (IsLocalPathDelim(c
))
2026 return ((char *) last
);
2027 } /* StrRFindLocalPathDelim */
2028 #endif /* Windows */
2034 gl_set_home_dir(const char *homedir
)
2038 const char *homedrive
, *homepath
;
2045 if (gl_home_dir
!= NULL
) {
2050 if (homedir
== NULL
) {
2052 homedrive
= getenv("HOMEDRIVE");
2053 homepath
= getenv("HOMEPATH");
2054 if ((homedrive
!= NULL
) && (homepath
!= NULL
)) {
2055 len
= strlen(homedrive
) + strlen(homepath
) + 1;
2056 gl_home_dir
= (char *) malloc(len
);
2057 if (gl_home_dir
!= NULL
) {
2058 strcpy(gl_home_dir
, homedrive
);
2059 strcat(gl_home_dir
, homepath
);
2065 if (GetWindowsDirectory(wdir
, sizeof(wdir
) - 1) < 1)
2066 (void) strncpy(wdir
, ".", sizeof(wdir
));
2067 else if (wdir
[1] == ':') {
2073 cp
= (char *) getlogin();
2075 cp
= (char *) getenv("LOGNAME");
2077 cp
= (char *) getenv("USER");
2083 pw
= getpwuid(getuid());
2085 return; /* hell with it */
2086 homedir
= pw
->pw_dir
;
2090 len
= strlen(homedir
) + /* NUL */ 1;
2091 gl_home_dir
= (char *) malloc(len
);
2092 if (gl_home_dir
!= NULL
) {
2093 memcpy(gl_home_dir
, homedir
, len
);
2095 } /* gl_set_home_dir */
2100 char *gl_getpass(const char *const prompt
, char *const pass
, int dsize
)
2106 memset(pass
, 0, (size_t) sizeof(dsize
));
2110 /* Display the prompt first. */
2111 if ((prompt
!= NULL
) && (prompt
[0] != '\0'))
2115 while ((c
= gl_getc()) != (-1)) {
2116 if ((c
== '\r') || (c
== '\n'))
2118 if ((c
== '\010') || (c
== '\177')) {
2126 } else if (cp
< (pass
+ dsize
)) {
2140 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
2141 ZeroMemory(pass
, (DWORD
) sizeof(dsize
));
2144 if ((prompt
!= NULL
) && (prompt
[0] != '\0'))
2149 if ((c
== '\r') || (c
== '\n'))
2151 if ((c
== '\010') || (c
== '\177')) {
2159 } else if (cp
< (pass
+ dsize
)) {
2167 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
2171 #endif /* __windows__ */
2172 #endif /* ! __unix__ */
2181 gl_local_filename_completion_proc(const char *start
, int idx
)
2183 static DIR *dir
= NULL
;
2184 static int filepfxoffset
;
2185 static size_t filepfxlen
;
2187 const char *filepfx
;
2188 struct dirent
*dent
;
2190 const char *dirtoopen
, *name
;
2197 /* shouldn't get here! */
2205 cp
= _StrRFindLocalPathDelim(start
);
2207 dirtoopen
= LOCAL_PATH_DELIM_STR
; /* root dir */
2209 } else if (cp
== NULL
) {
2213 len
= strlen(start
) + 1;
2214 dirtoopen1
= (char *) malloc(len
);
2215 if (dirtoopen1
== NULL
)
2217 memcpy(dirtoopen1
, start
, len
);
2219 dirtoopen1
[len
] = '\0';
2220 dirtoopen
= dirtoopen1
;
2221 filepfxoffset
= (int) ((cp
+ 1) - start
);
2224 if (strcmp(dirtoopen
, "~") == 0) {
2225 if (gl_home_dir
== NULL
)
2226 gl_set_home_dir(NULL
);
2227 if (gl_home_dir
== NULL
)
2229 dirtoopen
= gl_home_dir
;
2232 dir
= opendir(dirtoopen
);
2233 if (dirtoopen1
!= NULL
)
2236 filepfx
= start
+ filepfxoffset
;
2237 filepfxlen
= strlen(filepfx
);
2241 /* assumes "start" is same for each iteration. */
2242 filepfx
= start
+ filepfxoffset
;
2245 dent
= readdir(dir
);
2252 /* There was exactly one match.
2253 * In this special case, we
2254 * want to append a / instead
2257 cp
= gl_matchlist
[0];
2258 if ((cp
[0] == '~') && ((cp
[1] == '\0') || (IsLocalPathDelim(cp
[1])))) {
2259 len
= strlen(cp
+ 1) + /* NUL */ 1;
2260 len2
= strlen(gl_home_dir
);
2261 if (IsLocalPathDelim(gl_home_dir
[len2
- 1]))
2263 cp
= (char *) realloc(gl_matchlist
[0], len
+ len2
);
2265 cp
= gl_matchlist
[0];
2267 memmove(cp
+ len2
, cp
+ 1, len
);
2268 memcpy(cp
, gl_home_dir
, len2
);
2269 gl_matchlist
[0] = cp
;
2272 if ((lstat(cp
, &st
) == 0) && (S_ISDIR(st
.st_mode
)))
2273 gl_completion_exact_match_extra_char
= LOCAL_PATH_DELIM
;
2278 name
= dent
->d_name
;
2279 if ((name
[0] == '.') && ((name
[1] == '\0') || ((name
[1] == '.') && (name
[2] == '\0'))))
2280 continue; /* Skip . and .. */
2282 if ((filepfxlen
== 0) || (strncmp(name
, filepfx
, filepfxlen
) == 0)) {
2285 cp
= (char *) malloc(filepfxoffset
+ len
+ 1 /* spare */ + 1 /* NUL */);
2287 if (filepfxoffset
> 0)
2288 memcpy(cp
, start
, (size_t) filepfxoffset
);
2289 memcpy(cp
+ filepfxoffset
, name
, len
+ 1);
2296 } /* gl_local_filename_completion_proc */
2298 #endif /* __unix__ */
2307 gl_local_filename_completion_proc(const char *start
, int idx
)
2309 static HANDLE searchHandle
= NULL
;
2310 static int filepfxoffset
;
2311 static size_t filepfxlen
;
2313 WIN32_FIND_DATA ffd
;
2316 const char *filepfx
;
2317 const char *dirtoopen
, *name
;
2318 char *dirtoopen1
, *dirtoopen2
;
2322 if (searchHandle
!= NULL
) {
2323 /* shouldn't get here! */
2324 FindClose(searchHandle
);
2325 searchHandle
= NULL
;
2330 if (searchHandle
== NULL
) {
2333 cp
= _StrRFindLocalPathDelim(start
);
2335 dirtoopen
= LOCAL_PATH_DELIM_STR
; /* root dir */
2337 } else if (cp
== NULL
) {
2341 len
= strlen(start
) + 1;
2342 dirtoopen1
= (char *) malloc(len
);
2343 if (dirtoopen1
== NULL
)
2345 memcpy(dirtoopen1
, start
, len
);
2347 dirtoopen1
[len
] = '\0';
2348 dirtoopen
= dirtoopen1
;
2349 filepfxoffset
= (int) ((cp
+ 1) - start
);
2352 if (strcmp(dirtoopen
, "~") == 0) {
2353 if (gl_home_dir
== NULL
)
2354 gl_set_home_dir(NULL
);
2355 if (gl_home_dir
== NULL
)
2357 dirtoopen
= gl_home_dir
;
2360 len
= strlen(dirtoopen
);
2361 dirtoopen2
= (char *) malloc(len
+ 8);
2362 if (dirtoopen2
== NULL
) {
2363 if (dirtoopen1
!= NULL
)
2368 memcpy(dirtoopen2
, dirtoopen
, len
+ 1);
2369 if (dirtoopen2
[len
- 1] == LOCAL_PATH_DELIM
)
2370 memcpy(dirtoopen2
+ len
, "*.*", (size_t) 4);
2372 memcpy(dirtoopen2
+ len
, "\\*.*", (size_t) 5);
2374 /* "Open" the directory. */
2375 memset(&ffd
, 0, sizeof(ffd
));
2376 searchHandle
= FindFirstFile(dirtoopen2
, &ffd
);
2379 if (dirtoopen1
!= NULL
)
2382 if (searchHandle
== INVALID_HANDLE_VALUE
) {
2386 filepfx
= start
+ filepfxoffset
;
2387 filepfxlen
= strlen(filepfx
);
2389 /* assumes "start" is same for each iteration. */
2390 filepfx
= start
+ filepfxoffset
;
2396 name
= ffd
.cFileName
;
2397 if ((name
[0] == '.') && ((name
[1] == '\0') || ((name
[1] == '.') && (name
[2] == '\0'))))
2398 goto next
; /* Skip . and .. */
2400 if ((filepfxlen
== 0) || (_strnicmp(name
, filepfx
, filepfxlen
) == 0)) {
2403 cp
= (char *) malloc(filepfxoffset
+ len
+ 4 /* spare */ + 1 /* NUL */);
2405 if (filepfxoffset
> 0)
2406 memcpy(cp
, start
, filepfxoffset
);
2407 memcpy(cp
+ filepfxoffset
, name
, len
+ 1);
2408 if (ffd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
2409 /* Embed file type with name. */
2410 c2
= cp
+ filepfxoffset
+ len
+ 1;
2415 c2
= cp
+ filepfxoffset
+ len
+ 1;
2424 if (!FindNextFile(searchHandle
, &ffd
)) {
2425 dwErr
= GetLastError();
2426 if (dwErr
!= ERROR_NO_MORE_FILES
) {
2427 FindClose(searchHandle
);
2428 searchHandle
= NULL
;
2433 FindClose(searchHandle
);
2434 searchHandle
= NULL
;
2437 /* There was exactly one match.
2438 * In this special case, we
2439 * want to append a \ instead
2442 cp
= gl_matchlist
[0];
2443 ch
= (char) cp
[strlen(cp
) + 2];
2444 if (ch
== (char) 'd')
2445 gl_completion_exact_match_extra_char
= LOCAL_PATH_DELIM
;
2447 if ((cp
[0] == '~') && ((cp
[1] == '\0') || (IsLocalPathDelim(cp
[1])))) {
2448 len
= strlen(cp
+ 1) + /* NUL */ 1;
2449 len2
= strlen(gl_home_dir
);
2450 if (IsLocalPathDelim(gl_home_dir
[len2
- 1]))
2452 cp
= (char *) realloc(gl_matchlist
[0], len
+ len2
+ 4);
2454 cp
= gl_matchlist
[0];
2456 memmove(cp
+ len2
, cp
+ 1, len
);
2457 memcpy(cp
, gl_home_dir
, len2
);
2458 c2
= cp
+ len
+ len2
;
2462 gl_matchlist
[0] = cp
;
2470 } /* gl_local_filename_completion_proc */
2472 #endif /* __windows__ */