merge trunk head (37902)
[reactos.git] / rosapps / applications / net / ncftp / ncftp / getline.c
1 /* Based on: "$Id$"; */
2 static const char copyright[] = "getline: Copyright (C) 1991, 1992, 1993, Chris Thewalt";
3
4 /*
5 * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
6 *
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
12 * implied warranty.
13 *
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
18 */
19
20 /*
21 * Note: This version has been updated by Mike Gleason <mgleason@ncftp.com>
22 */
23
24 #if defined(WIN32) || defined(_WINDOWS)
25 # include <windows.h>
26 # include <sys/types.h>
27 # include <sys/stat.h>
28 # include <errno.h>
29 # include <conio.h>
30 # include <io.h>
31 # include <fcntl.h>
32 # define strcasecmp stricmp
33 # define strncasecmp strnicmp
34 # define sleep(a) Sleep(a * 1000)
35 # ifndef S_ISREG
36 # define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
37 # define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
38 # endif
39 # ifndef open
40 # define open _open
41 # define write _write
42 # define read _read
43 # define close _close
44 # define lseek _lseek
45 # define stat _stat
46 # define lstat _stat
47 # define fstat _fstat
48 # define dup _dup
49 # define utime _utime
50 # define utimbuf _utimbuf
51 # endif
52 # ifndef unlink
53 # define unlink remove
54 # endif
55 # define NO_SIGNALS 1
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
63 #else
64 # ifndef __unix__
65 # define __unix__ 1
66 # endif
67 # if defined(AIX) || defined(_AIX)
68 # define _ALL_SOURCE 1
69 # endif
70 # if defined(HAVE_CONFIG_H)
71 # include <config.h>
72 # else
73 # /* guess */
74 # define HAVE_TERMIOS_H 1
75 # define HAVE_UNISTD_H 1
76 # endif
77 # ifdef HAVE_UNISTD_H
78 # include <unistd.h>
79 # endif
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>
85 # endif
86 # include <fcntl.h>
87 # include <errno.h>
88 # include <dirent.h>
89 # include <pwd.h>
90 # ifdef HAVE_TERMIOS_H /* use HAVE_TERMIOS_H interface */
91 # include <termios.h>
92 struct termios new_termios, old_termios;
93 # else /* not HAVE_TERMIOS_H */
94 # include <sys/ioctl.h>
95 # ifdef TIOCSETN /* use BSD interface */
96 # include <sgtty.h>
97 struct sgttyb new_tty, old_tty;
98 struct tchars tch;
99 struct ltchars ltch;
100 # else /* use SYSV interface */
101 # include <termio.h>
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)
110 #endif
111
112 /********************* C library headers ********************************/
113
114 #include <stdio.h>
115 #include <string.h>
116 #ifdef HAVE_STRINGS_H
117 # include <strings.h>
118 #endif
119 #include <stdlib.h>
120 #include <ctype.h>
121 #include <signal.h>
122
123 #define _getline_c_ 1
124 #include "getline.h"
125
126 static int gl_tab(char *buf, int offset, int *loc, size_t bufsize);
127
128 /******************** external interface *********************************/
129
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 */
140
141 /******************** internal interface *********************************/
142
143
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;
164
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 */
170
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);
186
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 */
191
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 */
197
198 static int gl_do_tab_completion(char *buf, int *loc, size_t bufsize, int tabtab);
199
200 /************************ nonportable part *********************************/
201
202 #ifdef MSDOS
203 #include <bios.h>
204 #endif
205
206 static void
207 gl_char_init(void) /* turn off input echo */
208 {
209 #ifdef __unix__
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];
214 # ifdef VSUSP
215 gl_suspc = old_termios.c_cc[VSUSP];
216 # endif
217 # ifdef VDSUSP
218 gl_dsuspc = old_termios.c_cc[VDSUSP];
219 # endif
220 }
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;
232 }
233 ioctl(0, TIOCGLTC, &ltch);
234 gl_suspc = ltch.t_suspc;
235 gl_dsuspc = ltch.t_dsuspc;
236 ioctl(0, TIOCGETP, &old_tty);
237 new_tty = old_tty;
238 new_tty.sg_flags |= RAW;
239 new_tty.sg_flags &= ~ECHO;
240 ioctl(0, TIOCSETN, &new_tty);
241 # else /* SYSV */
242 if (ioctl(0, TCGETA, &old_termio) == 0) {
243 gl_intrc = old_termio.c_cc[VINTR];
244 gl_quitc = old_termio.c_cc[VQUIT];
245 }
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);
253 # endif
254 #endif /* __unix__ */
255 }
256
257 static void
258 gl_char_cleanup(void) /* undo effects of gl_char_init */
259 {
260 #ifdef __unix__
261 # ifdef HAVE_TERMIOS_H
262 tcsetattr(0, TCSANOW, &old_termios);
263 # elif defined(TIOCSETN) /* BSD */
264 ioctl(0, TIOCSETN, &old_tty);
265 # else /* SYSV */
266 ioctl(0, TCSETA, &old_termio);
267 # endif
268 #endif /* __unix__ */
269 }
270
271
272
273 int
274 gl_get_result(void)
275 {
276 return (gl_result);
277 } /* gl_get_result */
278
279
280
281
282 #if defined(MSDOS) || defined(__windows__)
283
284 #define K_UP 0x48
285 #define K_DOWN 0x50
286 #define K_LEFT 0x4B
287 #define K_RIGHT 0x4D
288 #define K_DELETE 0x53
289 #define K_INSERT 0x52
290 #define K_HOME 0x47
291 #define K_END 0x4F
292 #define K_PGUP 0x49
293 #define K_PGDN 0x51
294
295 int pc_keymap(int c)
296 {
297 switch (c) {
298 case K_UP:
299 case K_PGUP:
300 c = 16; /* up -> ^P */
301 break;
302 case K_DOWN:
303 case K_PGDN:
304 c = 14; /* down -> ^N */
305 break;
306 case K_LEFT:
307 c = 2; /* left -> ^B */
308 break;
309 case K_RIGHT:
310 c = 6; /* right -> ^F */
311 break;
312 case K_END:
313 c = 5; /* end -> ^E */
314 break;
315 case K_HOME:
316 c = 1; /* home -> ^A */
317 break;
318 case K_INSERT:
319 c = 15; /* insert -> ^O */
320 break;
321 case K_DELETE:
322 c = 4; /* del -> ^D */
323 break;
324 default:
325 c = 0; /* make it garbage */
326 }
327 return c;
328 }
329 #endif /* defined(MSDOS) || defined(__windows__) */
330
331 static int
332 gl_getc(void)
333 /* get a character without echoing it to screen */
334 {
335 int c;
336 #ifdef __unix__
337 char ch;
338 #endif
339
340 #ifdef __unix__
341 ch = '\0';
342 while ((c = (int) read(0, &ch, 1)) == -1) {
343 if (errno != EINTR)
344 break;
345 }
346 if (c != (-1))
347 c = (int) ch;
348 #endif /* __unix__ */
349 #ifdef MSDOS
350 c = _bios_keybrd(_NKEYBRD_READ);
351 if ((c & 0377) == 224) {
352 c = pc_keymap((c >> 8) & 0377);
353 } else {
354 c &= 0377;
355 }
356 #endif /* MSDOS */
357 #ifdef __windows__
358 c = (int) _getch();
359 if ((c == 0) || (c == 0xE0)) {
360 /* Read key code */
361 c = (int) _getch();
362 c = pc_keymap(c);
363 } else if (c == '\r') {
364 /* Note: we only get \r from the console,
365 * and not a matching \n.
366 */
367 c = '\n';
368 }
369 #endif
370 return c;
371 }
372
373
374
375 #ifdef __unix__
376
377 static int
378 gl_getcx(int tlen)
379 /* Get a character without echoing it to screen, timing out
380 * after tlen tenths of a second.
381 */
382 {
383 int c, result;
384 char ch;
385 fd_set ss;
386 struct timeval tv;
387
388 for (errno = 0;;) {
389 FD_ZERO(&ss);
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);
394 if (result == 1) {
395 /* ready */
396 break;
397 } else if (result == 0) {
398 errno = ETIMEDOUT;
399 return (-2);
400 } else if (errno != EINTR) {
401 return (-1);
402 }
403 }
404
405 for (errno = 0;;) {
406 c = (int) read(0, &ch, 1);
407 if (c == 1)
408 return ((int) ch);
409 if (errno != EINTR)
410 break;
411 }
412
413 return (-1);
414 } /* gl_getcx */
415
416 #endif /* __unix__ */
417
418
419
420
421 #ifdef __windows__
422
423 static int
424 gl_getcx(int tlen)
425 {
426 int i, c;
427
428 c = (-2);
429 tlen -= 2; /* Adjust for 200ms overhead */
430 if (tlen < 1)
431 tlen = 1;
432 for (i=0; i<tlen; i++) {
433 if (_kbhit()) {
434 c = (int) _getch();
435 if ((c == 0) || (c == 0xE0)) {
436 /* Read key code */
437 c = (int) _getch();
438 c = pc_keymap(c);
439 break;
440 }
441 }
442 (void) SleepEx((DWORD) (tlen * 100), FALSE);
443 }
444 return (c);
445 } /* gl_getcx */
446
447 #endif /* __windows__ */
448
449
450
451
452 static void
453 gl_putc(int c)
454 {
455 char ch = (char) (unsigned char) c;
456
457 write(1, &ch, 1);
458 if (ch == '\n') {
459 ch = '\r';
460 write(1, &ch, 1); /* RAW mode needs '\r', does not hurt */
461 }
462 }
463
464 /******************** fairly portable part *********************************/
465
466 static void
467 gl_puts(const char *const buf)
468 {
469 int len;
470
471 if (buf) {
472 len = (int) strlen(buf);
473 write(1, buf, len);
474 }
475 }
476
477 static void
478 gl_error(const char *const buf)
479 {
480 int len = (int) strlen(buf);
481
482 gl_cleanup();
483 write(2, buf, len);
484 exit(1);
485 }
486
487 static void
488 gl_init(void)
489 /* set up variables and terminal */
490 {
491 const char *cp;
492 int w;
493
494 if (gl_init_done < 0) { /* -1 only on startup */
495 cp = (const char *) getenv("COLUMNS");
496 if (cp != NULL) {
497 w = atoi(cp);
498 if (w > 20)
499 gl_setwidth(w);
500 }
501 cp = (const char *) getenv("ROWS");
502 if (cp != NULL) {
503 w = atoi(cp);
504 if (w > 10)
505 gl_setheight(w);
506 }
507 hist_init();
508 }
509 if (isatty(0) == 0 || isatty(1) == 0)
510 gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
511 gl_char_init();
512 gl_init_done = 1;
513 }
514
515 static void
516 gl_cleanup(void)
517 /* undo effects of gl_init, as necessary */
518 {
519 if (gl_init_done > 0)
520 gl_char_cleanup();
521 gl_init_done = 0;
522 #ifdef __windows__
523 Sleep(40);
524 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
525 #endif
526 }
527
528
529 static void
530 gl_check_inputrc_for_vi(void)
531 {
532 FILE *fp;
533 char path[256];
534
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.
543 */
544 gl_set_home_dir(NULL);
545 if (gl_home_dir == NULL)
546 return;
547
548 #ifdef HAVE_SNPRINTF
549 snprintf(path, sizeof(path), "%s/%s", gl_home_dir, ".inputrc");
550 #else
551 if (sizeof(path) >= (strlen(gl_home_dir) + strlen("/.inputrc")))
552 return;
553
554 sprintf(path, "%s%s", gl_home_dir, "/.inputrc");
555 #endif
556
557 fp = fopen(
558 path,
559 #if defined(__windows__) || defined(MSDOS)
560 "rt"
561 #else
562 "r"
563 #endif
564 );
565
566 if (fp == NULL)
567 return;
568
569 while (fgets(path, sizeof(path) - 1, fp) != NULL) {
570 if ((strstr(path, "editing-mode") != NULL) && (strstr(path, "vi") != NULL)) {
571 gl_vi_preferred = 1;
572 break;
573 }
574 }
575
576 (void) fclose(fp);
577 } /* gl_check_inputrc_for_vi */
578
579
580
581 void
582 gl_setwidth(int w)
583 {
584 if (w > 250)
585 w = 250;
586 if (w > 20) {
587 gl_termw = w;
588 gl_scroll = w / 3;
589 } else {
590 gl_error("\n*** Error: minimum screen width is 21\n");
591 }
592 } /* gl_setwidth */
593
594
595
596 void
597 gl_setheight(int w)
598 {
599 if (w > 10) {
600 gl_termh = w;
601 } else {
602 gl_error("\n*** Error: minimum screen height is 10\n");
603 }
604 } /* gl_setheight */
605
606
607
608
609 char *
610 getline(char *prompt)
611 {
612 int c, loc, tmp, lastch;
613 int vi_count, count;
614 int vi_delete;
615 char vi_countbuf[32];
616 char *cp;
617
618 #ifdef __unix__
619 int sig;
620 #endif
621
622 /* We'll change the result code only if something happens later. */
623 gl_result = GL_OK;
624
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.
628 */
629 gl_vi_mode = 0;
630 vi_count = 0;
631 vi_delete = 0;
632 if (gl_vi_preferred < 0) {
633 gl_vi_preferred = 0;
634 cp = (char *) getenv("EDITOR");
635 if (cp != NULL)
636 gl_vi_preferred = (strstr(cp, "vi") != NULL);
637 if (gl_vi_preferred == 0)
638 gl_check_inputrc_for_vi();
639 }
640
641 gl_init();
642 gl_prompt = (prompt)? prompt : "";
643 gl_buf[0] = 0;
644 if (gl_in_hook)
645 gl_in_hook(gl_buf);
646 gl_fixup(gl_prompt, -2, GL_BUF_SIZE);
647 lastch = 0;
648
649 #ifdef __windows__
650 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
651 #endif
652
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.
660 */
661 vi:
662 for (count = 0; ; ) {
663 if (isdigit(c)) {
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));
669 }
670 switch (c) {
671 case 'b':
672 gl_word(-1);
673 break;
674 case 'w':
675 if (vi_delete) {
676 gl_killword(1);
677 } else {
678 gl_word(1);
679 }
680 break;
681 case 'h': /* left */
682 if (vi_delete) {
683 if (gl_pos > 0) {
684 gl_fixup(gl_prompt, -1, gl_pos-1);
685 gl_del(0, 1);
686 }
687 } else {
688 gl_fixup(gl_prompt, -1, gl_pos-1);
689 }
690 break;
691 case ' ':
692 case 'l': /* right */
693 if (vi_delete) {
694 gl_del(0, 1);
695 } else {
696 gl_fixup(gl_prompt, -1, gl_pos+1);
697 }
698 break;
699 case 'k': /* up */
700 strcpy(gl_buf, hist_prev());
701 if (gl_in_hook)
702 gl_in_hook(gl_buf);
703 gl_fixup(gl_prompt, 0, GL_BUF_SIZE);
704 break;
705 case 'j': /* down */
706 strcpy(gl_buf, hist_next());
707 if (gl_in_hook)
708 gl_in_hook(gl_buf);
709 gl_fixup(gl_prompt, 0, GL_BUF_SIZE);
710 break;
711 case 'd':
712 if (vi_delete == 1) {
713 gl_kill(0);
714 vi_count = 1;
715 vi_delete = 0;
716 gl_vi_mode = 0;
717 goto vi_break;
718 }
719 vi_delete = 1;
720 goto vi_break;
721 case '^': /* start of line */
722 if (vi_delete) {
723 vi_count = gl_pos;
724 gl_fixup(gl_prompt, -1, 0);
725 for (c = 0; c < vi_count; c++) {
726 if (gl_cnt > 0)
727 gl_del(0, 0);
728 }
729 vi_count = 1;
730 vi_delete = 0;
731 } else {
732 gl_fixup(gl_prompt, -1, 0);
733 }
734 break;
735 case '$': /* end of line */
736 if (vi_delete) {
737 gl_kill(gl_pos);
738 } else {
739 loc = (int) strlen(gl_buf);
740 if (loc > 1)
741 loc--;
742 gl_fixup(gl_prompt, -1, loc);
743 }
744 break;
745 case 'p': /* paste after */
746 gl_fixup(gl_prompt, -1, gl_pos+1);
747 gl_yank();
748 break;
749 case 'P': /* paste before */
750 gl_yank();
751 break;
752 case 'r': /* replace character */
753 gl_buf[gl_pos] = (char) gl_getc();
754 gl_fixup(gl_prompt, gl_pos, gl_pos);
755 vi_count = 1;
756 break;
757 case 'R':
758 gl_overwrite = 1;
759 gl_vi_mode = 0;
760 break;
761 case 'i':
762 case 'I':
763 gl_overwrite = 0;
764 gl_vi_mode = 0;
765 break;
766 case 'o':
767 case 'O':
768 case 'a':
769 case 'A':
770 gl_overwrite = 0;
771 gl_fixup(gl_prompt, -1, gl_pos+1);
772 gl_vi_mode = 0;
773 break;
774 }
775 count++;
776 if (count >= vi_count)
777 break;
778 }
779 vi_count = 1;
780 vi_delete = 0;
781 vi_break:
782 continue;
783 } else if (gl_search_mode) {
784 search_addchar(c);
785 } else {
786 gl_addchar(c);
787 }
788 } else {
789 if (gl_search_mode) {
790 if (c == '\033' || c == '\016' || c == '\020') {
791 search_term();
792 c = 0; /* ignore the character */
793 } else if (c == '\010' || c == '\177') {
794 search_addchar(-1); /* unwind search string */
795 c = 0;
796 } else if (c != '\022' && c != '\023') {
797 search_term(); /* terminate and handle char */
798 }
799 }
800 switch (c) {
801 case '\n': case '\r': /* newline */
802 gl_newline();
803 gl_cleanup();
804 return gl_buf;
805 case '\001': gl_fixup(gl_prompt, -1, 0); /* ^A */
806 break;
807 case '\002': gl_fixup(gl_prompt, -1, gl_pos-1); /* ^B */
808 break;
809 case '\004': /* ^D */
810 if (gl_cnt == 0) {
811 gl_buf[0] = 0;
812 gl_cleanup();
813 gl_putc('\n');
814 gl_result = GL_EOF;
815 return gl_buf;
816 } else {
817 gl_del(0, 1);
818 }
819 break;
820 case '\005': gl_fixup(gl_prompt, -1, gl_cnt); /* ^E */
821 break;
822 case '\006': gl_fixup(gl_prompt, -1, gl_pos+1); /* ^F */
823 break;
824 case '\010': case '\177': gl_del(-1, 0); /* ^H and DEL */
825 break;
826 case '\t': /* TAB */
827 if (gl_completion_proc) {
828 tmp = gl_pos;
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') {
835 c = 0;
836 lastch = 0;
837 }
838 } else if (gl_tab_hook) {
839 tmp = gl_pos;
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);
845 }
846 break;
847 case '\013': gl_kill(gl_pos); /* ^K */
848 break;
849 case '\014': gl_redraw(); /* ^L */
850 break;
851 case '\016': /* ^N */
852 strcpy(gl_buf, hist_next());
853 if (gl_in_hook)
854 gl_in_hook(gl_buf);
855 gl_fixup(gl_prompt, 0, GL_BUF_SIZE);
856 break;
857 case '\017': gl_overwrite = !gl_overwrite; /* ^O */
858 break;
859 case '\020': /* ^P */
860 strcpy(gl_buf, hist_prev());
861 if (gl_in_hook)
862 gl_in_hook(gl_buf);
863 gl_fixup(gl_prompt, 0, GL_BUF_SIZE);
864 break;
865 case '\022': search_back(1); /* ^R */
866 break;
867 case '\023': search_forw(1); /* ^S */
868 break;
869 case '\024': gl_transpose(); /* ^T */
870 break;
871 case '\025': gl_kill(0); /* ^U */
872 break;
873 case '\027': gl_killword(-1); /* ^W */
874 break;
875 case '\031': gl_yank(); /* ^Y */
876 break;
877 case '\033': /* ansi arrow keys */
878 c = gl_getcx(3);
879 if ((c == '[') || (c == 'O')) {
880 ansi:
881 switch(c = gl_getc()) {
882 case 'A': /* up */
883 strcpy(gl_buf, hist_prev());
884 if (gl_in_hook)
885 gl_in_hook(gl_buf);
886 gl_fixup(gl_prompt, 0, GL_BUF_SIZE);
887 break;
888 case 'B': /* down */
889 strcpy(gl_buf, hist_next());
890 if (gl_in_hook)
891 gl_in_hook(gl_buf);
892 gl_fixup(gl_prompt, 0, GL_BUF_SIZE);
893 break;
894 case 'C':
895 gl_fixup(gl_prompt, -1, gl_pos+1); /* right */
896 break;
897 case 'D':
898 gl_fixup(gl_prompt, -1, gl_pos-1); /* left */
899 break;
900 case '0':
901 case '1':
902 goto ansi;
903 default: gl_beep(); /* who knows */
904 break;
905 }
906 } else if ((gl_vi_preferred == 0) && ((c == 'f') || (c == 'F'))) {
907 gl_word(1);
908 } else if ((gl_vi_preferred == 0) && ((c == 'b') || (c == 'B'))) {
909 gl_word(-1);
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
916 * vi mode.
917 */
918 gl_kill(0);
919 } else
920 #endif
921 if (gl_vi_mode == 0) {
922 gl_vi_mode = 1;
923 vi_count = 1;
924 vi_delete = 0;
925 memset(vi_countbuf, 0, sizeof(vi_countbuf));
926 if (gl_pos > 0)
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.
930 */
931 if ((gl_cnt > 0) || (gl_vi_preferred == 1)) {
932 /* We still have to use the char read! */
933 goto vi;
934 }
935 gl_vi_mode = 0;
936 } else {
937 gl_beep();
938 }
939 }
940 break;
941 default: /* check for a terminal signal */
942 if (c > 0) { /* ignore 0 (reset above) */
943 if (c == gl_intrc) {
944 gl_result = GL_INTERRUPT;
945 gl_buf[0] = 0;
946 gl_cleanup();
947 #ifdef SIGINT
948 raise(SIGINT);
949 gl_init();
950 gl_redraw();
951 #endif
952 return gl_buf;
953 }
954
955 if (c == gl_quitc) {
956 gl_result = GL_INTERRUPT;
957 gl_buf[0] = 0;
958 gl_cleanup();
959 #ifdef SIGQUIT
960 raise(SIGQUIT);
961 gl_init();
962 gl_redraw();
963 #endif
964 return gl_buf;
965 }
966
967 #ifdef __unix__
968 if (c == gl_suspc || c == gl_dsuspc) {
969 #ifdef SIGTSTP
970 gl_result = GL_INTERRUPT;
971 gl_buf[0] = 0;
972 gl_cleanup();
973 sig = SIGTSTP;
974 kill(0, sig);
975 gl_init();
976 gl_redraw();
977 return gl_buf;
978 #endif
979 }
980 #endif /* __unix__ */
981 }
982 if (c > 0)
983 gl_beep();
984 break;
985 }
986 }
987 if (c > 0)
988 lastch = c;
989 }
990 gl_buf[0] = 0;
991 gl_cleanup();
992 return gl_buf;
993 }
994
995 static void
996 gl_addchar(int c)
997
998 /* adds the character c to the input buffer at current location */
999 {
1000 int i;
1001
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);
1009 } else {
1010 gl_buf[gl_pos] = (char) c;
1011 gl_extent = 1;
1012 gl_fixup(gl_prompt, gl_pos, gl_pos+1);
1013 }
1014 }
1015
1016 static void
1017 gl_yank(void)
1018 /* adds the kill buffer to the input buffer at current location */
1019 {
1020 int i, len;
1021
1022 len = (int) strlen(gl_killbuf);
1023 if (len > 0) {
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);
1032 } else {
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;
1037 }
1038 for (i=0; i < len; i++)
1039 gl_buf[gl_pos+i] = gl_killbuf[i];
1040 gl_extent = len;
1041 gl_fixup(gl_prompt, gl_pos, gl_pos+len);
1042 }
1043 } else
1044 gl_beep();
1045 }
1046
1047 static void
1048 gl_transpose(void)
1049 /* switch character under cursor and to left of cursor */
1050 {
1051 int c;
1052
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;
1057 gl_extent = 2;
1058 gl_fixup(gl_prompt, gl_pos-1, gl_pos);
1059 } else
1060 gl_beep();
1061 }
1062
1063 static void
1064 gl_newline(void)
1065 /*
1066 * Cleans up entire line before returning to caller. A \n is appended.
1067 * If line longer than screen, we redraw starting at beginning
1068 */
1069 {
1070 int change = gl_cnt;
1071 int len = gl_cnt;
1072 int loc = gl_width - 5; /* shifts line back to start position */
1073
1074 if (gl_cnt >= GL_BUF_SIZE - 1)
1075 gl_error("\n*** Error: getline(): input buffer overflow\n");
1076 if (gl_out_hook) {
1077 change = gl_out_hook(gl_buf);
1078 len = (int) strlen(gl_buf);
1079 }
1080 if (loc > len)
1081 loc = len;
1082 gl_fixup(gl_prompt, change, loc); /* must do this before appending \n */
1083 gl_buf[len] = '\n';
1084 gl_buf[len+1] = '\0';
1085 gl_putc('\n');
1086 }
1087
1088 static void
1089 gl_del(int loc, int killsave)
1090
1091 /*
1092 * Delete a character. The loc variable can be:
1093 * -1 : delete character to left of cursor
1094 * 0 : delete character under cursor
1095 */
1096 {
1097 int i, j;
1098
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';
1104 j = 1;
1105 }
1106 gl_buf[i] = gl_buf[i+1];
1107 }
1108 gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
1109 } else
1110 gl_beep();
1111 }
1112
1113 static void
1114 gl_kill(int pos)
1115
1116 /* delete from pos to the end of line */
1117 {
1118 if (pos < gl_cnt) {
1119 strcpy(gl_killbuf, gl_buf + pos);
1120 gl_buf[pos] = '\0';
1121 gl_fixup(gl_prompt, pos, pos);
1122 } else
1123 gl_beep();
1124 }
1125
1126 static void
1127 gl_killword(int direction)
1128 {
1129 int pos = gl_pos;
1130 int startpos = gl_pos;
1131 int tmp;
1132 int i;
1133
1134 if (direction > 0) { /* forward */
1135 while (!isspace(gl_buf[pos]) && pos < gl_cnt)
1136 pos++;
1137 while (isspace(gl_buf[pos]) && pos < gl_cnt)
1138 pos++;
1139 } else { /* backward */
1140 if (pos > 0)
1141 pos--;
1142 while (isspace(gl_buf[pos]) && pos > 0)
1143 pos--;
1144 while (!isspace(gl_buf[pos]) && pos > 0)
1145 pos--;
1146 if (pos < gl_cnt && isspace(gl_buf[pos])) /* move onto word */
1147 pos++;
1148 }
1149 if (pos < startpos) {
1150 tmp = pos;
1151 pos = startpos;
1152 startpos = tmp;
1153 }
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++)
1160 gl_del(0, 0);
1161 } /* gl_killword */
1162
1163 static void
1164 gl_word(int direction)
1165
1166 /* move forward or backword one word */
1167 {
1168 int pos = gl_pos;
1169
1170 if (direction > 0) { /* forward */
1171 while (!isspace(gl_buf[pos]) && pos < gl_cnt)
1172 pos++;
1173 while (isspace(gl_buf[pos]) && pos < gl_cnt)
1174 pos++;
1175 } else { /* backword */
1176 if (pos > 0)
1177 pos--;
1178 while (isspace(gl_buf[pos]) && pos > 0)
1179 pos--;
1180 while (!isspace(gl_buf[pos]) && pos > 0)
1181 pos--;
1182 if (pos < gl_cnt && isspace(gl_buf[pos])) /* move onto word */
1183 pos++;
1184 }
1185 gl_fixup(gl_prompt, -1, pos);
1186 }
1187
1188 static void
1189 gl_redraw(void)
1190 /* emit a newline, reset and redraw prompt and current input line */
1191 {
1192 if (gl_init_done > 0) {
1193 gl_putc('\n');
1194 gl_fixup(gl_prompt, -2, gl_pos);
1195 }
1196 }
1197
1198 static void
1199 gl_fixup(const char *prompt, int change, int cursor)
1200
1201
1202 /*
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.
1212 */
1213 {
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 */
1223 int i;
1224 int new_right = -1; /* alternate right bound, using gl_extent */
1225 int l1, l2;
1226
1227 if (change == -2) { /* reset */
1228 gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
1229 gl_putc('\r');
1230 gl_puts(prompt);
1231 strcpy(last_prompt, prompt);
1232 change = 0;
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);
1239 gl_putc('\r');
1240 gl_puts(prompt);
1241 gl_pos = gl_shift;
1242 gl_width = gl_termw - l2;
1243 change = 0;
1244 }
1245 pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift; /* old length */
1246 backup = gl_pos - gl_shift;
1247 if (change >= 0) {
1248 gl_cnt = (int) strlen(gl_buf);
1249 if (change > gl_cnt)
1250 change = gl_cnt;
1251 }
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) {
1255 gl_beep();
1256 }
1257 }
1258 cursor = gl_cnt;
1259 }
1260 if (cursor < 0) {
1261 gl_beep();
1262 cursor = 0;
1263 }
1264 if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
1265 extra = 2; /* shift the scrolling boundary */
1266 else
1267 extra = 0;
1268 new_shift = cursor + extra + gl_scroll - gl_width;
1269 if (new_shift > 0) {
1270 new_shift /= gl_scroll;
1271 new_shift *= gl_scroll;
1272 } else
1273 new_shift = 0;
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;
1278 left = gl_shift;
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) {
1282 left = gl_shift;
1283 } else {
1284 left = change;
1285 backup = gl_pos - change;
1286 }
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;
1291 }
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++)
1296 gl_putc('\b');
1297 if (left == gl_shift && off_left) {
1298 gl_putc('$');
1299 left++;
1300 }
1301 for (i=left; i < new_right; i++)
1302 gl_putc(gl_buf[i]);
1303 gl_pos = new_right;
1304 if (off_right && new_right == right) {
1305 gl_putc('$');
1306 gl_pos++;
1307 } else {
1308 for (i=0; i < pad; i++) /* erase remains of prev line */
1309 gl_putc(' ');
1310 gl_pos += pad;
1311 }
1312 }
1313 i = gl_pos - cursor; /* move to final cursor location */
1314 if (i > 0) {
1315 while (i--)
1316 gl_putc('\b');
1317 } else {
1318 for (i=gl_pos; i < cursor; i++)
1319 gl_putc(gl_buf[i]);
1320 }
1321 gl_pos = cursor;
1322 }
1323
1324 static int
1325 gl_tab(char *buf, int offset, int *loc, size_t bufsize)
1326 /* default tab handler, acts like tabstops every 8 cols */
1327 {
1328 int i, count, len;
1329
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)
1337 buf[*loc+i] = ' ';
1338 i = *loc;
1339 *loc = i + count;
1340 return i;
1341 }
1342
1343 /******************* History stuff **************************************/
1344
1345 #ifndef HIST_SIZE
1346 #define HIST_SIZE 100
1347 #endif
1348
1349 static int hist_pos = 0, hist_last = 0;
1350 static char *hist_buf[HIST_SIZE];
1351 static char hist_empty_elem[2] = "";
1352
1353 static void
1354 hist_init(void)
1355 {
1356 int i;
1357
1358 hist_buf[0] = hist_empty_elem;
1359 for (i=1; i < HIST_SIZE; i++)
1360 hist_buf[i] = (char *)0;
1361 }
1362
1363 void
1364 gl_histadd(char *buf)
1365 {
1366 static char *prev = 0;
1367 char *p = buf;
1368 int len;
1369
1370 /* in case we call gl_histadd() before we call getline() */
1371 if (gl_init_done < 0) { /* -1 only on startup */
1372 hist_init();
1373 gl_init_done = 0;
1374 }
1375 while (*p == ' ' || *p == '\t' || *p == '\n')
1376 p++;
1377 if (*p) {
1378 len = (int) strlen(buf);
1379 if (strchr(p, '\n')) /* previously line already has NL stripped */
1380 len--;
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]);
1388 }
1389 hist_buf[hist_last] = hist_empty_elem;
1390 }
1391 }
1392 hist_pos = hist_last;
1393 }
1394
1395 static char *
1396 hist_prev(void)
1397 /* loads previous hist entry into input buffer, sticks on first */
1398 {
1399 char *p = 0;
1400 int next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
1401
1402 if (hist_buf[hist_pos] != 0 && next != hist_last) {
1403 hist_pos = next;
1404 p = hist_buf[hist_pos];
1405 }
1406 if (p == 0) {
1407 p = hist_empty_elem;
1408 gl_beep();
1409 }
1410 return p;
1411 }
1412
1413 static char *
1414 hist_next(void)
1415 /* loads next hist entry into input buffer, clears on last */
1416 {
1417 char *p = 0;
1418
1419 if (hist_pos != hist_last) {
1420 hist_pos = (hist_pos+1) % HIST_SIZE;
1421 p = hist_buf[hist_pos];
1422 }
1423 if (p == 0) {
1424 p = hist_empty_elem;
1425 gl_beep();
1426 }
1427 return p;
1428 }
1429
1430 static char *
1431 hist_save(char *p)
1432
1433 /* makes a copy of the string */
1434 {
1435 char *s = 0;
1436 size_t len = strlen(p);
1437 char *nl = strpbrk(p, "\n\r");
1438
1439 if (nl) {
1440 if ((s = (char *) malloc(len)) != 0) {
1441 strncpy(s, p, len-1);
1442 s[len-1] = 0;
1443 }
1444 } else {
1445 if ((s = (char *) malloc(len+1)) != 0) {
1446 strcpy(s, p);
1447 }
1448 }
1449 if (s == 0)
1450 gl_error("\n*** Error: hist_save() failed on malloc\n");
1451 return s;
1452 }
1453
1454
1455
1456
1457 void
1458 gl_histsavefile(const char *const path)
1459 {
1460 FILE *fp;
1461 const char *p;
1462 int i, j;
1463
1464 fp = fopen(path,
1465 #if defined(__windows__) || defined(MSDOS)
1466 "wt"
1467 #else
1468 "w"
1469 #endif
1470 );
1471 if (fp != NULL) {
1472 for (i=2; i<HIST_SIZE; i++) {
1473 j = (hist_pos+i) % HIST_SIZE;
1474 p = hist_buf[j];
1475 if ((p == NULL) || (*p == '\0'))
1476 continue;
1477 fprintf(fp, "%s\n", p);
1478 }
1479 fclose(fp);
1480 }
1481 } /* gl_histsavefile */
1482
1483
1484
1485
1486 void
1487 gl_histloadfile(const char *const path)
1488 {
1489 FILE *fp;
1490 char line[256];
1491
1492 fp = fopen(path,
1493 #if defined(__windows__) || defined(MSDOS)
1494 "rt"
1495 #else
1496 "r"
1497 #endif
1498 );
1499 if (fp != NULL) {
1500 memset(line, 0, sizeof(line));
1501 while (fgets(line, sizeof(line) - 2, fp) != NULL) {
1502 gl_histadd(line);
1503 }
1504 fclose(fp);
1505 }
1506 } /* gl_histloadfile */
1507
1508
1509
1510
1511 /******************* Search stuff **************************************/
1512
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 */
1518
1519 static void
1520 search_update(int c)
1521 {
1522 if (c == 0) {
1523 search_pos = 0;
1524 search_string[0] = 0;
1525 search_prompt[0] = '?';
1526 search_prompt[1] = ' ';
1527 search_prompt[2] = 0;
1528 } else if (c > 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;
1535 search_pos++;
1536 } else {
1537 if (search_pos > 0) {
1538 search_pos--;
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;
1543 } else {
1544 gl_beep();
1545 hist_pos = hist_last;
1546 }
1547 }
1548 }
1549
1550 static void
1551 search_addchar(int c)
1552 {
1553 char *loc;
1554
1555 search_update(c);
1556 if (c < 0) {
1557 if (search_pos > 0) {
1558 hist_pos = search_last;
1559 } else {
1560 gl_buf[0] = 0;
1561 hist_pos = hist_last;
1562 }
1563 strcpy(gl_buf, hist_buf[hist_pos]);
1564 }
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) {
1569 search_forw(0);
1570 } else {
1571 search_back(0);
1572 }
1573 } else {
1574 gl_fixup(search_prompt, 0, 0);
1575 }
1576 }
1577
1578 static void
1579 search_term(void)
1580 {
1581 gl_search_mode = 0;
1582 if (gl_buf[0] == 0) /* not found, reset hist list */
1583 hist_pos = hist_last;
1584 if (gl_in_hook)
1585 gl_in_hook(gl_buf);
1586 gl_fixup(gl_prompt, 0, gl_pos);
1587 }
1588
1589 static void
1590 search_back(int new_search)
1591 {
1592 int found = 0;
1593 char *p, *loc;
1594
1595 search_forw_flg = 0;
1596 if (gl_search_mode == 0) {
1597 search_last = hist_pos = hist_last;
1598 search_update(0);
1599 gl_search_mode = 1;
1600 gl_buf[0] = 0;
1601 gl_fixup(search_prompt, 0, 0);
1602 } else if (search_pos > 0) {
1603 while (!found) {
1604 p = hist_prev();
1605 if (*p == 0) { /* not found, done looking */
1606 gl_buf[0] = 0;
1607 gl_fixup(search_prompt, 0, 0);
1608 found = 1;
1609 } else if ((loc = strstr(p, search_string)) != 0) {
1610 strcpy(gl_buf, p);
1611 gl_fixup(search_prompt, 0, (int) (loc - p));
1612 if (new_search)
1613 search_last = hist_pos;
1614 found = 1;
1615 }
1616 }
1617
1618 } else {
1619 gl_beep();
1620 }
1621 }
1622
1623 static void
1624 search_forw(int new_search)
1625 {
1626 int found = 0;
1627 char *p, *loc;
1628
1629 search_forw_flg = 1;
1630 if (gl_search_mode == 0) {
1631 search_last = hist_pos = hist_last;
1632 search_update(0);
1633 gl_search_mode = 1;
1634 gl_buf[0] = 0;
1635 gl_fixup(search_prompt, 0, 0);
1636 } else if (search_pos > 0) {
1637 while (!found) {
1638 p = hist_next();
1639 if (*p == 0) { /* not found, done looking */
1640 gl_buf[0] = 0;
1641 gl_fixup(search_prompt, 0, 0);
1642 found = 1;
1643 } else if ((loc = strstr(p, search_string)) != 0) {
1644 strcpy(gl_buf, p);
1645 gl_fixup(search_prompt, 0, (int) (loc - p));
1646 if (new_search)
1647 search_last = hist_pos;
1648 found = 1;
1649 }
1650 }
1651 } else {
1652 gl_beep();
1653 }
1654 }
1655
1656
1657 static void
1658 gl_beep(void)
1659 {
1660 #ifdef __windows__
1661 MessageBeep(MB_OK);
1662 #else
1663 gl_putc('\007');
1664 #endif
1665 } /* gl_beep */
1666
1667
1668
1669 static int
1670 gl_display_matches_sort_proc(const void *a, const void *b)
1671 {
1672 return (strcasecmp(
1673 * ((const char **) a),
1674 * ((const char **) b)
1675 ));
1676 } /* gl_display_matches_sort_proc */
1677
1678
1679
1680 static void
1681 gl_display_matches(int nused)
1682 {
1683 char buf[256];
1684 char buf2[256];
1685 size_t ilen, imaxlen;
1686 int i, j, k, l;
1687 int glen, allmatch;
1688 int nmax, ncol, colw, nrow;
1689 char *cp1, *cp2, *lim, *itemp;
1690
1691 gl_putc('\n');
1692 if (nused == 0) {
1693 gl_beep();
1694 gl_puts(" (no matches)");
1695 gl_putc('\n');
1696 } else {
1697 qsort(gl_matchlist, (size_t) nused, sizeof(char *), gl_display_matches_sort_proc);
1698
1699 /* Find the greatest amount that matches. */
1700 for (glen = 0; ; glen++) {
1701 allmatch = 1;
1702 for (i=1; i<nused; i++) {
1703 if (gl_matchlist[0][glen] != gl_matchlist[i][glen]) {
1704 allmatch = 0;
1705 break;
1706 }
1707 }
1708 if (allmatch == 0)
1709 break;
1710 }
1711
1712 while (glen > 0) {
1713 if (!isalnum(gl_matchlist[0][glen - 1]))
1714 break;
1715 --glen;
1716 }
1717
1718 nmax = nused;
1719 imaxlen = strlen(gl_matchlist[0]);
1720 for (i=1; i<nused; i++) {
1721 ilen = strlen(gl_matchlist[i]);
1722 if (ilen > imaxlen)
1723 imaxlen = ilen;
1724 }
1725
1726 /* Subtract amount we'll skip for each item. */
1727 imaxlen -= glen;
1728
1729 ncol = (gl_termw - 8) / ((int) imaxlen + 2);
1730 if (ncol < 1)
1731 ncol = 1;
1732
1733 colw = (gl_termw - 8) / ncol;
1734 nrow = nmax / ncol;
1735 if ((nused % ncol) != 0)
1736 nrow++;
1737
1738 if (nrow > (gl_termh - 4)) {
1739 nrow = gl_termh - 4;
1740 nmax = ncol * nrow;
1741 }
1742
1743 for (i=0; i<(int) sizeof(buf2); i++)
1744 buf2[i] = ' ';
1745
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) {
1749 if (k >= nmax)
1750 continue;
1751 itemp = gl_matchlist[k] + glen;
1752 cp1 = buf + l;
1753 lim = cp1 + (int) strlen(itemp);
1754 if (lim > (buf + sizeof(buf) - 1))
1755 continue;
1756 cp2 = itemp;
1757 while (cp1 < lim)
1758 *cp1++ = *cp2++;
1759 }
1760 for (cp1 = buf + sizeof(buf); *--cp1 == ' '; )
1761 ;
1762 ++cp1;
1763 *cp1 = '\0';
1764 gl_puts(buf);
1765 gl_putc('\n');
1766 }
1767
1768 if (nused > nmax) {
1769 (void) sprintf(buf, " ... %d others omitted ...", (nused - nmax));
1770 gl_puts(buf);
1771 gl_putc('\n');
1772 }
1773 }
1774 gl_fixup(gl_prompt, -2, GL_BUF_SIZE);
1775 } /* gl_display_matches */
1776
1777
1778
1779
1780 static int
1781 gl_do_tab_completion(char *buf, int *loc, size_t bufsize, int tabtab)
1782 {
1783 char *startp;
1784 size_t startoff, amt;
1785 int c;
1786 int qmode;
1787 char *qstart;
1788 char *lastspacestart;
1789 char *cp;
1790 int ntoalloc, nused, nprocused, nalloced, i;
1791 char **newgl_matchlist;
1792 char *strtoadd, *strtoadd1;
1793 int addquotes;
1794 size_t llen, mlen, glen;
1795 int allmatch;
1796 char *curposp;
1797 size_t lenaftercursor;
1798 char *matchpfx;
1799 int wasateol;
1800 char ellipsessave[4];
1801
1802 /* Zero out the rest of the buffer, so we can move stuff around
1803 * and know we'll still be NUL-terminated.
1804 */
1805 llen = strlen(buf);
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);
1816 }
1817
1818 qmode = 0;
1819 qstart = NULL;
1820 lastspacestart = NULL;
1821 matchpfx = NULL;
1822
1823 cp = buf;
1824 while (cp < curposp) {
1825 c = (int) *cp++;
1826 if (c == '\0')
1827 break;
1828 if ((c == '"') || (c == '\'')) {
1829 if (qmode == c) {
1830 /* closing quote; end it. */
1831 qstart = NULL;
1832 qmode = 0;
1833 } else if (qmode != 0) {
1834 /* just treat it as a regular char. */
1835 } else {
1836 /* start new quote group. */
1837 qmode = c;
1838 qstart = cp - 1;
1839 }
1840 } else if ((isspace(c)) && (qmode == 0)) {
1841 /* found a non-quoted space. */
1842 lastspacestart = cp - 1;
1843 } else {
1844 /* regular char */
1845 }
1846 }
1847
1848 if (qstart != NULL)
1849 startp = qstart + 1;
1850 else if (lastspacestart != NULL)
1851 startp = lastspacestart + 1;
1852 else
1853 startp = buf;
1854
1855 cp = startp;
1856 mlen = (curposp - cp);
1857
1858 matchpfx = (char *) malloc(mlen + 1);
1859 memcpy(matchpfx, cp, mlen);
1860 matchpfx[mlen] = '\0';
1861
1862 #define GL_COMPLETE_VECTOR_BLOCK_SIZE 64
1863
1864 nused = 0;
1865 ntoalloc = GL_COMPLETE_VECTOR_BLOCK_SIZE;
1866 newgl_matchlist = (char **) malloc((size_t) (sizeof(char *) * (ntoalloc + 1)));
1867 if (newgl_matchlist == NULL) {
1868 free(matchpfx);
1869 gl_beep();
1870 return 0;
1871 }
1872 gl_matchlist = newgl_matchlist;
1873 nalloced = ntoalloc;
1874 for (i=nused; i<=nalloced; i++)
1875 gl_matchlist[i] = NULL;
1876
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]);
1886 free(gl_matchlist);
1887 gl_matchlist = NULL;
1888 gl_beep();
1889 free(matchpfx);
1890 return 0;
1891 }
1892 gl_matchlist = newgl_matchlist;
1893 nalloced = ntoalloc;
1894 for (i=nused; i<=nalloced; i++)
1895 gl_matchlist[i] = NULL;
1896 }
1897 cp = gl_completion_proc(matchpfx, nprocused);
1898 if (cp == NULL)
1899 break;
1900 if ((cp[0] == '.') && ((cp[1] == '\0') || ((cp[1] == '.') && (cp[2] == '\0'))))
1901 continue; /* Skip . and .. */
1902 gl_matchlist[nused++] = cp;
1903 }
1904
1905 if (gl_ellipses_during_completion != 0) {
1906 gl_fixup(gl_prompt, gl_pos, gl_pos);
1907 gl_puts(" ");
1908 }
1909
1910 /* We now have an array strings, whose last element is NULL. */
1911 strtoadd = NULL;
1912 strtoadd1 = NULL;
1913 amt = 0;
1914
1915 addquotes = (gl_filename_quoting_desired > 0) || ((gl_filename_quoting_desired < 0) && (gl_completion_proc == gl_local_filename_completion_proc));
1916
1917 if (nused == 1) {
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++) {
1926 allmatch = 1;
1927 for (i=1; i<nused; i++) {
1928 if (gl_matchlist[0][glen] != gl_matchlist[i][glen]) {
1929 allmatch = 0;
1930 break;
1931 }
1932 }
1933 if (allmatch == 0)
1934 break;
1935 }
1936 strtoadd1 = (char *) malloc(glen + 1);
1937 if (strtoadd1 != NULL) {
1938 memcpy(strtoadd1, gl_matchlist[0], glen);
1939 strtoadd1[glen] = '\0';
1940 strtoadd = strtoadd1;
1941 }
1942 }
1943
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 */);
1949 curposp++;
1950 *startp++ = (char) qmode;
1951 }
1952 }
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);
1960 if (nused == 1) {
1961 /* Exact match. */
1962 if (qmode != 0) {
1963 /* Finish the quoting. */
1964 memmove(curposp + 1, curposp, lenaftercursor + 1 /* NUL */);
1965 curposp++;
1966 buf[amt + startoff] = (char) qmode;
1967 amt++;
1968 }
1969 memmove(curposp + 1, curposp, lenaftercursor + 1 /* NUL */);
1970 curposp++;
1971 buf[amt + startoff] = (char) gl_completion_exact_match_extra_char;
1972 amt++;
1973 } else if ((!wasateol) && (!isspace(*curposp))) {
1974 /* Not a full match, but insert a
1975 * space for better readability.
1976 */
1977 memmove(curposp + 1, curposp, lenaftercursor + 1 /* NUL */);
1978 curposp++;
1979 buf[amt + startoff] = ' ';
1980 }
1981 *loc = (int) (startoff + amt);
1982
1983 if (strtoadd1 != NULL)
1984 free(strtoadd1);
1985 }
1986
1987 /* Don't need this any more. */
1988 for (i=0; i<nused; i++)
1989 free(gl_matchlist[i]);
1990 free(gl_matchlist);
1991 gl_matchlist = NULL;
1992 free(matchpfx);
1993
1994 return 0;
1995 } /* gl_do_tab_completion */
1996
1997
1998
1999
2000 void
2001 gl_tab_completion(gl_tab_completion_proc proc)
2002 {
2003 if (proc == NULL)
2004 proc = gl_local_filename_completion_proc; /* default proc */
2005 gl_completion_proc = proc;
2006 } /* gl_tab_completion */
2007
2008
2009
2010
2011 #ifndef _StrFindLocalPathDelim
2012 static char *
2013 _StrRFindLocalPathDelim(const char *src) /* TODO: optimize */
2014 {
2015 const char *last;
2016 int c;
2017
2018 last = NULL;
2019 for (;;) {
2020 c = *src++;
2021 if (c == '\0')
2022 break;
2023 if (IsLocalPathDelim(c))
2024 last = src - 1;
2025 }
2026
2027 return ((char *) last);
2028 } /* StrRFindLocalPathDelim */
2029 #endif /* Windows */
2030
2031
2032
2033
2034 void
2035 gl_set_home_dir(const char *homedir)
2036 {
2037 size_t len;
2038 #ifdef __windows__
2039 const char *homedrive, *homepath;
2040 char wdir[64];
2041 #else
2042 struct passwd *pw;
2043 char *cp;
2044 #endif
2045
2046 if (gl_home_dir != NULL) {
2047 free(gl_home_dir);
2048 gl_home_dir = NULL;
2049 }
2050
2051 if (homedir == NULL) {
2052 #ifdef __windows__
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);
2061 return;
2062 }
2063 }
2064
2065 wdir[0] = '\0';
2066 if (GetWindowsDirectory(wdir, sizeof(wdir) - 1) < 1)
2067 (void) strncpy(wdir, ".", sizeof(wdir));
2068 else if (wdir[1] == ':') {
2069 wdir[2] = '\\';
2070 wdir[3] = '\0';
2071 }
2072 homedir = wdir;
2073 #else
2074 cp = (char *) getlogin();
2075 if (cp == NULL) {
2076 cp = (char *) getenv("LOGNAME");
2077 if (cp == NULL)
2078 cp = (char *) getenv("USER");
2079 }
2080 pw = NULL;
2081 if (cp != NULL)
2082 pw = getpwnam(cp);
2083 if (pw == NULL)
2084 pw = getpwuid(getuid());
2085 if (pw == NULL)
2086 return; /* hell with it */
2087 homedir = pw->pw_dir;
2088 #endif
2089 }
2090
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);
2095 }
2096 } /* gl_set_home_dir */
2097
2098
2099
2100
2101 char *gl_getpass(const char *const prompt, char *const pass, int dsize)
2102 {
2103 #ifdef __unix__
2104 char *cp;
2105 int c;
2106
2107 memset(pass, 0, (size_t) sizeof(dsize));
2108 dsize--;
2109 gl_init();
2110
2111 /* Display the prompt first. */
2112 if ((prompt != NULL) && (prompt[0] != '\0'))
2113 gl_puts(prompt);
2114
2115 cp = pass;
2116 while ((c = gl_getc()) != (-1)) {
2117 if ((c == '\r') || (c == '\n'))
2118 break;
2119 if ((c == '\010') || (c == '\177')) {
2120 /* ^H and DEL */
2121 if (cp > pass) {
2122 *--cp = '\0';
2123 gl_putc('\010');
2124 gl_putc(' ');
2125 gl_putc('\010');
2126 }
2127 } else if (cp < (pass + dsize)) {
2128 gl_putc('*');
2129 *cp++ = c;
2130 }
2131 }
2132 *cp = '\0';
2133 gl_putc('\n');
2134 gl_cleanup();
2135 return (pass);
2136 #else
2137 #ifdef __windows__
2138 char *cp;
2139 int c;
2140
2141 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2142 ZeroMemory(pass, (DWORD) sizeof(dsize));
2143 dsize--;
2144
2145 if ((prompt != NULL) && (prompt[0] != '\0'))
2146 _cputs(prompt);
2147
2148 for (cp = pass;;) {
2149 c = (int) _getch();
2150 if ((c == '\r') || (c == '\n'))
2151 break;
2152 if ((c == '\010') || (c == '\177')) {
2153 /* ^H and DEL */
2154 if (cp > pass) {
2155 *--cp = '\0';
2156 _putch('\010');
2157 _putch(' ');
2158 _putch('\010');
2159 }
2160 } else if (cp < (pass + dsize)) {
2161 _putch('*');
2162 *cp++ = c;
2163 }
2164 }
2165 _putch('\r');
2166 _putch('\n');
2167 Sleep(40);
2168 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2169
2170 *cp = '\0';
2171 return (pass);
2172 #endif /* __windows__ */
2173 #endif /* ! __unix__ */
2174 } /* gl_getpass */
2175
2176
2177
2178
2179 #ifdef __unix__
2180
2181 char *
2182 gl_local_filename_completion_proc(const char *start, int idx)
2183 {
2184 static DIR *dir = NULL;
2185 static int filepfxoffset;
2186 static size_t filepfxlen;
2187
2188 const char *filepfx;
2189 struct dirent *dent;
2190 char *cp;
2191 const char *dirtoopen, *name;
2192 char *dirtoopen1;
2193 size_t len, len2;
2194 struct stat st;
2195
2196 if (idx == 0) {
2197 if (dir != NULL) {
2198 /* shouldn't get here! */
2199 closedir(dir);
2200 dir = NULL;
2201 }
2202 }
2203
2204 if (dir == NULL) {
2205 dirtoopen1 = NULL;
2206 cp = _StrRFindLocalPathDelim(start);
2207 if (cp == start) {
2208 dirtoopen = LOCAL_PATH_DELIM_STR; /* root dir */
2209 filepfxoffset = 1;
2210 } else if (cp == NULL) {
2211 dirtoopen = ".";
2212 filepfxoffset = 0;
2213 } else {
2214 len = strlen(start) + 1;
2215 dirtoopen1 = (char *) malloc(len);
2216 if (dirtoopen1 == NULL)
2217 return NULL;
2218 memcpy(dirtoopen1, start, len);
2219 len = (cp - start);
2220 dirtoopen1[len] = '\0';
2221 dirtoopen = dirtoopen1;
2222 filepfxoffset = (int) ((cp + 1) - start);
2223 }
2224
2225 if (strcmp(dirtoopen, "~") == 0) {
2226 if (gl_home_dir == NULL)
2227 gl_set_home_dir(NULL);
2228 if (gl_home_dir == NULL)
2229 return (NULL);
2230 dirtoopen = gl_home_dir;
2231 }
2232
2233 dir = opendir(dirtoopen);
2234 if (dirtoopen1 != NULL)
2235 free(dirtoopen1);
2236
2237 filepfx = start + filepfxoffset;
2238 filepfxlen = strlen(filepfx);
2239 }
2240
2241 if (dir != NULL) {
2242 /* assumes "start" is same for each iteration. */
2243 filepfx = start + filepfxoffset;
2244
2245 for (;;) {
2246 dent = readdir(dir);
2247 if (dent == NULL) {
2248 /* no more items */
2249 closedir(dir);
2250 dir = NULL;
2251
2252 if (idx == 1) {
2253 /* There was exactly one match.
2254 * In this special case, we
2255 * want to append a / instead
2256 * of a space.
2257 */
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]))
2263 len2--;
2264 cp = (char *) realloc(gl_matchlist[0], len + len2);
2265 if (cp == NULL) {
2266 cp = gl_matchlist[0];
2267 } else {
2268 memmove(cp + len2, cp + 1, len);
2269 memcpy(cp, gl_home_dir, len2);
2270 gl_matchlist[0] = cp;
2271 }
2272 }
2273 if ((lstat(cp, &st) == 0) && (S_ISDIR(st.st_mode)))
2274 gl_completion_exact_match_extra_char = LOCAL_PATH_DELIM;
2275 }
2276 return NULL;
2277 }
2278
2279 name = dent->d_name;
2280 if ((name[0] == '.') && ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0'))))
2281 continue; /* Skip . and .. */
2282
2283 if ((filepfxlen == 0) || (strncmp(name, filepfx, filepfxlen) == 0)) {
2284 /* match */
2285 len = strlen(name);
2286 cp = (char *) malloc(filepfxoffset + len + 1 /* spare */ + 1 /* NUL */);
2287 *cp = '\0';
2288 if (filepfxoffset > 0)
2289 memcpy(cp, start, (size_t) filepfxoffset);
2290 memcpy(cp + filepfxoffset, name, len + 1);
2291 return (cp);
2292 }
2293 }
2294 }
2295
2296 return NULL;
2297 } /* gl_local_filename_completion_proc */
2298
2299 #endif /* __unix__ */
2300
2301
2302
2303
2304
2305 #ifdef __windows__
2306
2307 char *
2308 gl_local_filename_completion_proc(const char *start, int idx)
2309 {
2310 static HANDLE searchHandle = NULL;
2311 static int filepfxoffset;
2312 static size_t filepfxlen;
2313
2314 WIN32_FIND_DATA ffd;
2315 DWORD dwErr;
2316 char *cp, *c2, ch;
2317 const char *filepfx;
2318 const char *dirtoopen, *name;
2319 char *dirtoopen1, *dirtoopen2;
2320 size_t len, len2;
2321
2322 if (idx == 0) {
2323 if (searchHandle != NULL) {
2324 /* shouldn't get here! */
2325 FindClose(searchHandle);
2326 searchHandle = NULL;
2327 }
2328 }
2329
2330
2331 if (searchHandle == NULL) {
2332 dirtoopen1 = NULL;
2333 dirtoopen2 = NULL;
2334 cp = _StrRFindLocalPathDelim(start);
2335 if (cp == start) {
2336 dirtoopen = LOCAL_PATH_DELIM_STR; /* root dir */
2337 filepfxoffset = 1;
2338 } else if (cp == NULL) {
2339 dirtoopen = ".";
2340 filepfxoffset = 0;
2341 } else {
2342 len = strlen(start) + 1;
2343 dirtoopen1 = (char *) malloc(len);
2344 if (dirtoopen1 == NULL)
2345 return NULL;
2346 memcpy(dirtoopen1, start, len);
2347 len = (cp - start);
2348 dirtoopen1[len] = '\0';
2349 dirtoopen = dirtoopen1;
2350 filepfxoffset = (int) ((cp + 1) - start);
2351 }
2352
2353 if (strcmp(dirtoopen, "~") == 0) {
2354 if (gl_home_dir == NULL)
2355 gl_set_home_dir(NULL);
2356 if (gl_home_dir == NULL)
2357 return (NULL);
2358 dirtoopen = gl_home_dir;
2359 }
2360
2361 len = strlen(dirtoopen);
2362 dirtoopen2 = (char *) malloc(len + 8);
2363 if (dirtoopen2 == NULL) {
2364 if (dirtoopen1 != NULL)
2365 free(dirtoopen1);
2366 return NULL;
2367 }
2368
2369 memcpy(dirtoopen2, dirtoopen, len + 1);
2370 if (dirtoopen2[len - 1] == LOCAL_PATH_DELIM)
2371 memcpy(dirtoopen2 + len, "*.*", (size_t) 4);
2372 else
2373 memcpy(dirtoopen2 + len, "\\*.*", (size_t) 5);
2374
2375 /* "Open" the directory. */
2376 memset(&ffd, 0, sizeof(ffd));
2377 searchHandle = FindFirstFile(dirtoopen2, &ffd);
2378
2379 free(dirtoopen2);
2380 if (dirtoopen1 != NULL)
2381 free(dirtoopen1);
2382
2383 if (searchHandle == INVALID_HANDLE_VALUE) {
2384 return NULL;
2385 }
2386
2387 filepfx = start + filepfxoffset;
2388 filepfxlen = strlen(filepfx);
2389 } else {
2390 /* assumes "start" is same for each iteration. */
2391 filepfx = start + filepfxoffset;
2392 goto next;
2393 }
2394
2395 for (;;) {
2396
2397 name = ffd.cFileName;
2398 if ((name[0] == '.') && ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0'))))
2399 goto next; /* Skip . and .. */
2400
2401 if ((filepfxlen == 0) || (strnicmp(name, filepfx, filepfxlen) == 0)) {
2402 /* match */
2403 len = strlen(name);
2404 cp = (char *) malloc(filepfxoffset + len + 4 /* spare */ + 1 /* NUL */);
2405 *cp = '\0';
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;
2412 *c2++ = '\0';
2413 *c2++ = 'd';
2414 *c2 = '\0';
2415 } else {
2416 c2 = cp + filepfxoffset + len + 1;
2417 *c2++ = '\0';
2418 *c2++ = '-';
2419 *c2 = '\0';
2420 }
2421 return (cp);
2422 }
2423
2424 next:
2425 if (!FindNextFile(searchHandle, &ffd)) {
2426 dwErr = GetLastError();
2427 if (dwErr != ERROR_NO_MORE_FILES) {
2428 FindClose(searchHandle);
2429 searchHandle = NULL;
2430 return NULL;
2431 }
2432
2433 /* no more items */
2434 FindClose(searchHandle);
2435 searchHandle = NULL;
2436
2437 if (idx == 1) {
2438 /* There was exactly one match.
2439 * In this special case, we
2440 * want to append a \ instead
2441 * of a space.
2442 */
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;
2447
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]))
2452 len2--;
2453 cp = (char *) realloc(gl_matchlist[0], len + len2 + 4);
2454 if (cp == NULL) {
2455 cp = gl_matchlist[0];
2456 } else {
2457 memmove(cp + len2, cp + 1, len);
2458 memcpy(cp, gl_home_dir, len2);
2459 c2 = cp + len + len2;
2460 *c2++ = '\0';
2461 *c2++ = ch;
2462 *c2 = '\0';
2463 gl_matchlist[0] = cp;
2464 }
2465 }
2466 }
2467 break;
2468 }
2469 }
2470 return (NULL);
2471 } /* gl_local_filename_completion_proc */
2472
2473 #endif /* __windows__ */