2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley Software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
19 * Tenex style file name recognition, .. and more.
21 * Author: Ken Greer, Sept. 1975, CMU.
22 * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
26 #include <sys/types.h>
29 #include "sh.tconst.h"
38 extern DIR *opendir_(tchar
*);
40 static char *BELL
= "\07";
41 static char *CTRLR
= "^R\n";
43 typedef enum {LIST
, RECOGNIZE
} COMMAND
;
45 static jmp_buf osetexit
; /* saved setexit() state */
46 static struct termios tty_save
; /* saved terminal state */
47 static struct termios tty_new
; /* new terminal state */
49 static int is_prefix(tchar
*, tchar
*);
50 static int is_suffix(tchar
*, tchar
*);
51 static int ignored(tchar
*);
54 * Put this here so the binary can be patched with adb to enable file
55 * completion by default. Filec controls completion, nobeep controls
56 * ringing the terminal bell on incomplete expansions.
65 tprintf("TRACE- setup_tty()\n");
68 omask
= sigblock(sigmask(SIGINT
));
71 * The shell makes sure that the tty is not in some weird state
72 * and fixes it if it is. But it should be noted that the
73 * tenex routine will not work correctly in CBREAK or RAW mode
74 * so this code below is, therefore, mandatory.
76 * Also, in order to recognize the ESC (filename-completion)
77 * character, set EOL to ESC. This way, ESC will terminate
78 * the line, but still be in the input stream.
79 * EOT (filename list) will also terminate the line,
80 * but will not appear in the input stream.
82 * The getexit/setexit contortions ensure that the
83 * tty state will be restored if the user types ^C.
85 (void) ioctl(SHIN
, TCGETS
, (char *)&tty_save
);
88 (void) ioctl(SHIN
, TCSETSW
, (char *)&tty_save
);
93 tty_new
.c_cc
[VEOL
] = ESC
;
94 tty_new
.c_iflag
|= IMAXBEL
| BRKINT
| IGNPAR
;
95 tty_new
.c_lflag
|= ICANON
;
96 tty_new
.c_lflag
|= ECHOCTL
;
97 tty_new
.c_oflag
&= ~OCRNL
;
98 (void) ioctl(SHIN
, TCSETSW
, (char *)&tty_new
);
101 * Reset terminal state to what user had when invoked
103 (void) ioctl(SHIN
, TCSETSW
, (char *)&tty_save
);
106 (void) sigsetmask(omask
);
112 extern char *tgetstr();
114 static char area
[256];
115 static int been_here
= 0;
121 tprintf("TRACE- termchars()\n");
127 if ((term
= getenv("TERM")) == NULL
)
129 if (tgetent(bp
, term
) != 1)
131 if (s
= tgetstr("vb", &ap
)) /* Visible Bell */
136 * Move back to beginning of current line
144 tprintf("TRACE- back_to_col_1()\n");
146 omask
= sigblock(sigmask(SIGINT
));
147 (void) write(SHOUT
, "\r", 1);
148 (void) sigsetmask(omask
);
152 * Push string contents back into tty queue
155 pushback(tchar
*string
, int echoflag
)
159 int omask
, retry
= 0;
162 tprintf("TRACE- pushback()\n");
164 omask
= sigblock(sigmask(SIGINT
));
167 tty
.c_lflag
&= ~ECHO
;
170 (void) ioctl(SHIN
, TCSETSF
, (char *)&tty
);
172 for (p
= string
; *p
; p
++) {
173 char mbc
[MB_LEN_MAX
];
174 int i
, j
= wctomb(mbc
, (wchar_t)*p
);
177 /* Error! But else what can we do? */
180 for (i
= 0; i
< j
; ++i
) {
181 if (ioctl(SHIN
, TIOCSTI
, mbc
+ i
) != 0 &&
185 /* probably no worth retrying any more */
190 if (tty
.c_lflag
!= tty_new
.c_lflag
)
191 (void) ioctl(SHIN
, TCSETS
, (char *)&tty_new
);
192 (void) sigsetmask(omask
);
196 * Concatenate src onto tail of des.
197 * Des is a string whose maximum length is count.
198 * Always null terminate.
201 catn(tchar
*des
, tchar
*src
, int count
)
204 tprintf("TRACE- catn()\n");
207 while (--count
>= 0 && *des
)
210 if ((*des
++ = *src
++) == '\0')
219 return (a
> b
? a
: b
);
223 * Like strncpy but always leave room for trailing \0
224 * and always null terminate.
227 copyn(tchar
*des
, tchar
*src
, int count
)
231 tprintf("TRACE- copyn()\n");
234 if ((*des
++ = *src
++) == '\0')
243 fcompare(tchar
**file1
, tchar
**file2
)
247 tprintf("TRACE- fcompare()\n");
249 return (strcoll_(*file1
, *file2
));
253 filetype(tchar
*dir
, tchar
*file
, int nosym
)
255 tchar path
[MAXPATHLEN
+ 1];
259 tprintf("TRACE- filetype()\n");
262 catn(strcpy_(path
, dir
), file
, MAXPATHLEN
);
264 if (stat_(path
, &statb
) < 0)
267 if (lstat_(path
, &statb
) < 0)
270 if ((statb
.st_mode
& S_IFMT
) == S_IFLNK
)
272 if ((statb
.st_mode
& S_IFMT
) == S_IFDIR
)
274 if (((statb
.st_mode
& S_IFMT
) == S_IFREG
) &&
275 (statb
.st_mode
& 011))
282 * Print sorted down columns
285 print_by_column(tchar
*dir
, tchar
*items
[], int count
, int looking_for_command
)
287 int i
, rows
, r
, c
, maxwidth
= 0, columns
;
290 tprintf("TRACE- print_by_column()\n");
292 for (i
= 0; i
< count
; i
++)
293 maxwidth
= max(maxwidth
, tswidth(items
[i
]));
295 /* for the file tag and space */
296 maxwidth
+= looking_for_command
? 1 : 2;
297 columns
= max(78 / maxwidth
, 1);
298 rows
= (count
+ (columns
- 1)) / columns
;
300 for (r
= 0; r
< rows
; r
++) {
301 for (c
= 0; c
< columns
; c
++) {
307 * Print filename followed by
308 * '@' or '/' or '*' or ' '
310 printf("%t", items
[i
]);
311 w
= tswidth(items
[i
]);
312 if (!looking_for_command
) {
314 (tchar
) filetype(dir
, items
[i
], 0));
317 if (c
< columns
- 1) /* last column? */
318 for (; w
< maxwidth
; w
++)
327 * Expand file name with possible tilde usage
330 * home_directory_of_person/mumble
333 tilde(tchar
*new, tchar
*old
)
337 static tchar person
[40];
338 char person_
[40]; /* work */
339 tchar
*pw_dir
; /* work */
342 tprintf("TRACE- tilde()\n");
345 return (strcpy_(new, old
));
347 for (p
= person
, o
= &old
[1]; *o
&& *o
!= '/'; *p
++ = *o
++)
350 if (person
[0] == '\0')
351 (void) strcpy_(new, value(S_home
/* "home" */));
353 pw
= getpwnam(tstostr(person_
, person
));
356 pw_dir
= strtots((tchar
*)NULL
, pw
->pw_dir
); /* allocate */
357 (void) strcpy_(new, pw_dir
);
358 xfree(pw_dir
); /* free it */
360 (void) strcat_(new, o
);
365 * Cause pending line to be printed
371 struct termios tty_pending
;
374 tprintf("TRACE- sim_retypr()\n");
376 tty_pending
= tty_new
;
377 tty_pending
.c_lflag
|= PENDIN
;
379 (void) ioctl(SHIN
, TCSETS
, (char *)&tty_pending
);
382 tprintf("TRACE- sim_retype()\n");
384 (void) write(SHOUT
, CTRLR
, strlen(CTRLR
));
396 (void) write(SHOUT
, buf
, 1);
406 tprintf("TRACE- beep()\n");
408 if (adrof(S_nobeep
/* "nobeep" */) == 0)
409 (void) tputs(BELL
, 0, beep_outc
);
413 * Erase that silly ^[ and print the recognized part of the string.
416 print_recognized_stuff(tchar
*recognized_part
)
418 int unit
= didfds
? 1 : SHOUT
;
421 tprintf("TRACE- print_recognized_stuff()\n");
425 * An optimized erasing of that silly ^[
427 * One would think that line speeds have become fast enough that this
428 * isn't necessary, but it turns out that the visual difference is
432 switch (tswidth(recognized_part
)) {
434 /* erase two characters: ^[ */
435 write(unit
, "\b\b \b\b", sizeof "\b\b \b\b" - 1);
439 /* overstrike the ^, erase the [ */
440 write(unit
, "\b\b", 2);
441 printf("%t", recognized_part
);
442 write(unit
, " \b\b", 4);
446 /* overstrike both characters ^[ */
447 write(unit
, "\b\b", 2);
448 printf("%t", recognized_part
);
455 * Parse full path in file into 2 parts: directory and file names
456 * Should leave final slash (/) at end of dir.
459 extract_dir_and_name(tchar
*path
, tchar
*dir
, tchar
*name
)
464 tprintf("TRACE- extract_dir_and_name()\n");
466 p
= rindex_(path
, '/');
468 copyn(name
, path
, MAXNAMLEN
);
471 copyn(name
, ++p
, MAXNAMLEN
);
472 copyn(dir
, path
, p
- path
);
477 getentry(DIR *dir_fd
, int looking_for_lognames
)
482 * For char * -> tchar * Conversion
484 static tchar strbuf
[MAXNAMLEN
+1];
487 tprintf("TRACE- getentry()\n");
489 if (looking_for_lognames
) {
490 if ((pw
= getpwent()) == NULL
)
492 return (strtots(strbuf
, pw
->pw_name
));
494 if (dirp
= readdir(dir_fd
))
495 return (strtots(strbuf
, dirp
->d_name
));
500 free_items(tchar
**items
)
505 tprintf("TRACE- free_items()\n");
507 for (i
= 0; items
[i
]; i
++)
509 xfree((char *)items
);
512 #define FREE_ITEMS(items) { \
515 omask = sigblock(sigmask(SIGINT));\
518 (void) sigsetmask(omask);\
522 * Perform a RECOGNIZE or LIST command on string "word".
525 search2(tchar
*word
, COMMAND command
, int max_word_length
)
527 static tchar
**items
= NULL
;
529 int numitems
= 0, ignoring
= TRUE
, nignored
= 0;
530 int name_length
, looking_for_lognames
;
531 tchar tilded_dir
[MAXPATHLEN
+ 1], dir
[MAXPATHLEN
+ 1];
532 tchar name
[MAXNAMLEN
+ 1], extended_name
[MAXNAMLEN
+1];
534 #define MAXITEMS 1024
536 tprintf("TRACE- search2()\n");
542 looking_for_lognames
= (*word
== '~') && (index_(word
, '/') == NULL
);
543 if (looking_for_lognames
) {
545 copyn(name
, &word
[1], MAXNAMLEN
); /* name sans ~ */
547 extract_dir_and_name(word
, dir
, name
);
548 if (tilde(tilded_dir
, dir
) == 0)
550 dir_fd
= opendir_(*tilded_dir
? tilded_dir
: S_DOT
/* "." */);
555 again
: /* search for matches */
556 name_length
= strlen_(name
);
557 for (numitems
= 0; entry
= getentry(dir_fd
, looking_for_lognames
); ) {
558 if (!is_prefix(name
, entry
))
560 /* Don't match . files on null prefix match */
561 if (name_length
== 0 && entry
[0] == '.' &&
562 !looking_for_lognames
)
564 if (command
== LIST
) {
565 if (numitems
>= MAXITEMS
) {
566 printf("\nYikes!! Too many %s!!\n",
567 looking_for_lognames
?
568 "names in password file":"files");
572 items
= (tchar
**)xcalloc(sizeof (items
[1]),
574 items
[numitems
] = (tchar
*)xalloc((unsigned)(strlen_(entry
) + 1) * sizeof (tchar
));
575 copyn(items
[numitems
], entry
, MAXNAMLEN
);
577 } else { /* RECOGNIZE command */
578 if (ignoring
&& ignored(entry
))
580 else if (recognize(extended_name
,
581 entry
, name_length
, ++numitems
))
585 if (ignoring
&& numitems
== 0 && nignored
> 0) {
588 if (looking_for_lognames
)
595 if (looking_for_lognames
)
598 unsetfd(dir_fd
->dd_fd
);
601 if (command
== RECOGNIZE
&& numitems
> 0) {
602 if (looking_for_lognames
)
603 copyn(word
, S_TIL
/* "~" */, 1);
605 /* put back dir part */
606 copyn(word
, dir
, max_word_length
);
607 /* add extended name */
608 catn(word
, extended_name
, max_word_length
);
611 if (command
== LIST
) {
612 qsort((char *)items
, numitems
, sizeof (items
[1]),
613 (int (*)(const void *, const void *))fcompare
);
615 * Never looking for commands in this version, so final
616 * argument forced to 0. If command name completion is
617 * reinstated, this must change.
619 print_by_column(looking_for_lognames
? NULL
: tilded_dir
,
628 * Object: extend what user typed up to an ambiguity.
630 * On first match, copy full entry (assume it'll be the only match)
631 * On subsequent matches, shorten extended_name to the first
632 * character mismatch between extended_name and entry.
633 * If we shorten it back to the prefix length, stop searching.
636 recognize(tchar
*extended_name
, tchar
*entry
, int name_length
, int numitems
)
640 tprintf("TRACE- recognize()\n");
642 if (numitems
== 1) /* 1st match */
643 copyn(extended_name
, entry
, MAXNAMLEN
);
644 else { /* 2nd and subsequent matches */
649 for (ent
= entry
; *x
&& *x
== *ent
++; x
++, len
++)
651 *x
= '\0'; /* Shorten at 1st char diff */
652 if (len
== name_length
) /* Ambiguous to prefix? */
653 return (-1); /* So stop now and save time */
659 * Return true if check items initial chars in template
660 * This differs from PWB imatch in that if check is null
664 is_prefix(tchar
*check
, tchar
*template)
667 tprintf("TRACE- is_prefix()\n");
673 while (*check
++ == *template++);
678 * Return true if the chars in template appear at the
679 * end of check, i.e., are its suffix.
682 is_suffix(tchar
*check
, tchar
*template)
687 tprintf("TRACE- is_suffix()\n");
689 for (c
= check
; *c
++; )
691 for (t
= template; *t
++; )
696 if (c
== check
|| *--t
!= *--c
)
702 tenex(tchar
*inputline
, int inputline_size
)
704 int numitems
, num_read
, should_retype
;
708 tprintf("TRACE- tenex()\n");
713 should_retype
= FALSE
;
714 while ((i
= read_(SHIN
, inputline
+num_read
, inputline_size
-num_read
))
716 static tchar
*delims
= S_DELIM
/* " '\"\t;&<>()|`" */;
717 tchar
*str_end
, *word_start
, last_char
;
723 inputline
[num_read
] = '\0';
724 last_char
= inputline
[num_read
- 1] & TRIM
;
727 * read_() can return more than requested size if there
728 * is multibyte character at the end.
730 if ((num_read
>= inputline_size
) || (last_char
== '\n'))
733 str_end
= &inputline
[num_read
];
734 if (last_char
== ESC
) {
736 *--str_end
= '\0'; /* wipe out trailing ESC */
741 tty
.c_lflag
&= ~ECHO
;
742 (void) ioctl(SHIN
, TCSETSF
, (char *)&tty
);
747 * Find LAST occurence of a delimiter in the inputline.
748 * The word start is one character past it.
750 for (word_start
= str_end
; word_start
> inputline
;
752 if (index_(delims
, word_start
[-1]) ||
753 isauxsp(word_start
[-1]))
756 space_left
= inputline_size
- (word_start
- inputline
) - 1;
757 numitems
= search2(word_start
, command
, space_left
);
760 * Tabs in the input line cause trouble after a pushback.
761 * tty driver won't backspace over them because column
762 * positions are now incorrect. This is solved by retyping
765 if (index_(inputline
, '\t')) { /* tab tchar in input line? */
767 should_retype
= TRUE
;
769 if (command
== LIST
) /* Always retype after a LIST */
770 should_retype
= TRUE
;
773 pushback(inputline
, should_retype
);
774 num_read
= 0; /* chars will be reread */
775 should_retype
= FALSE
;
778 * Avoid a race condition by echoing what we're recognized
779 * _after_ pushing back the command line. This way, if the
780 * user waits until seeing this output before typing more
781 * stuff, the resulting keystrokes won't race with the STIed
782 * input we've pushed back. (Of course, if the user types
783 * ahead, the race still exists and it's quite possible that
784 * the pushed back input line will interleave with the
785 * keystrokes in unexpected ways.)
787 if (command
== RECOGNIZE
) {
788 /* print from str_end on */
789 print_recognized_stuff(str_end
);
790 if (numitems
!= 1) /* Beep = No match/ambiguous */
799 ignored(tchar
*entry
)
805 tprintf("TRACE- ignored()\n");
807 if ((vp
= adrof(S_fignore
/* "fignore" */)) == NULL
||
808 (cp
= vp
->vec
) == NULL
)
810 for (; *cp
!= NULL
; cp
++)
811 if (is_suffix(entry
, *cp
))