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