* Copyright (c) 1980, 1991 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)file.c 5.17 (Berkeley) 6/8/91";
* Tenex style file name recognition, .. and more.
* Author: Ken Greer, Sept. 1975, CMU.
* Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
static void setup_tty
__P((int));
static void back_to_col_1
__P((void));
static void pushback
__P((Char
*));
static void catn
__P((Char
*, Char
*, int));
static void copyn
__P((Char
*, Char
*, int));
static Char filetype
__P((Char
*, Char
*));
static void print_by_column
__P((Char
*, Char
*[], int));
static Char
*tilde
__P((Char
*, Char
*));
static void retype
__P((void));
static void beep
__P((void));
static void print_recognized_stuff
__P((Char
*));
static void extract_dir_and_name
__P((Char
*, Char
*, Char
*));
static Char
*getentry
__P((DIR *, int));
static void free_items
__P((Char
**));
static int tsearch
__P((Char
*, COMMAND
, int));
static int recognize
__P((Char
*, Char
*, int, int));
static int is_prefix
__P((Char
*, Char
*));
static int is_suffix
__P((Char
*, Char
*));
static int ignored
__P((Char
*));
* Put this here so the binary can be patched with adb to enable file
* completion by default. Filec controls completion, nobeep controls
* ringing the terminal bell on incomplete expansions.
static struct termios tchars
;
(void) tcgetattr(SHIN
, &tchars
);
if (tchars
.c_lflag
& ICANON
)
tchars
.c_lflag
|= ICANON
;
(void) tcsetattr(SHIN
, on
, &tchars
);
tchars
.c_cc
[VEOL
] = _POSIX_VDISABLE
;
(void) tcsetattr(SHIN
, TCSANOW
, &tchars
);
* Move back to beginning of current line
struct termios tty
, tty_normal
;
omask
= sigblock(sigmask(SIGINT
));
(void) tcgetattr(SHOUT
, &tty
);
(void) tcsetattr(SHOUT
, TCSANOW
, &tty
);
(void) write(SHOUT
, "\r", 1);
(void) tcsetattr(SHOUT
, TCSANOW
, &tty_normal
);
(void) sigsetmask(omask
);
* Push string contents back into tty queue
struct termios tty
, tty_normal
;
omask
= sigblock(sigmask(SIGINT
));
(void) tcgetattr(SHOUT
, &tty
);
tty
.c_lflag
&= ~(ECHOKE
| ECHO
| ECHOE
| ECHOK
| ECHONL
| ECHOPRT
| ECHOCTL
);
(void) tcsetattr(SHOUT
, TCSANOW
, &tty
);
for (p
= string
; c
= *p
; p
++)
(void) ioctl(SHOUT
, TIOCSTI
, (ioctl_t
) & c
);
(void) tcsetattr(SHOUT
, TCSANOW
, &tty_normal
);
(void) sigsetmask(omask
);
* Concatenate src onto tail of des.
* Des is a string whose maximum length is count.
register Char
*des
, *src
;
while (--count
>= 0 && *des
)
if ((*des
++ = *src
++) == 0)
* Like strncpy but always leave room for trailing \0
* and always null terminate.
register Char
*des
, *src
;
if ((*des
++ = *src
++) == 0)
catn(Strcpy(path
, dir
), file
, sizeof(path
) / sizeof(Char
));
if (lstat(short2str(path
), &statb
) == 0) {
switch (statb
.st_mode
& S_IFMT
) {
if (stat(short2str(path
), &statb
) == 0 && /* follow it out */
if (statb
.st_mode
& 0111)
static struct winsize win
;
* Print sorted down columns
print_by_column(dir
, items
, count
)
register int i
, rows
, r
, c
, maxwidth
= 0, columns
;
if (ioctl(SHOUT
, TIOCGWINSZ
, (ioctl_t
) & win
) < 0 || win
.ws_col
== 0)
for (i
= 0; i
< count
; i
++)
maxwidth
= maxwidth
> (r
= Strlen(items
[i
])) ? maxwidth
: r
;
maxwidth
+= 2; /* for the file tag and space */
columns
= win
.ws_col
/ maxwidth
;
rows
= (count
+ (columns
- 1)) / columns
;
for (r
= 0; r
< rows
; r
++) {
for (c
= 0; c
< columns
; c
++) {
xprintf("%s", short2str(items
[i
]));
xputchar(dir
? filetype(dir
, items
[i
]) : ' ');
if (c
< columns
- 1) { /* last column? */
w
= Strlen(items
[i
]) + 1;
for (; w
< maxwidth
; w
++)
* Expand file name with possible tilde usage
* home_directory_of_person/mumble
register struct passwd
*pw
;
return (Strcpy(new, old
));
for (p
= person
, o
= &old
[1]; *o
&& *o
!= '/'; *p
++ = *o
++);
(void) Strcpy(new, value(STRhome
));
pw
= getpwnam(short2str(person
));
(void) Strcpy(new, str2short(pw
->pw_dir
));
* Cause pending line to be printed
(void) tcgetattr(SHOUT
, &tty
);
(void) tcsetattr(SHOUT
, TCSANOW
, &tty
);
if (adrof(STRnobeep
) == 0)
(void) write(SHOUT
, "\007", 1);
* Erase that silly ^[ and
* print the recognized part of the string
print_recognized_stuff(recognized_part
)
/* An optimized erasing of that silly ^[ */
switch (Strlen(recognized_part
)) {
case 0: /* erase two Characters: ^[ */
case 1: /* overstrike the ^, erase the [ */
xprintf("%s", short2str(recognized_part
));
default: /* overstrike both Characters ^[ */
xprintf("%s", short2str(recognized_part
));
* Parse full path in file into 2 parts: directory and file names
* Should leave final slash (/) at end of dir.
extract_dir_and_name(path
, dir
, name
)
copyn(name
, path
, MAXNAMLEN
);
copyn(name
, ++p
, MAXNAMLEN
);
copyn(dir
, path
, p
- path
);
getentry(dir_fd
, looking_for_lognames
)
int looking_for_lognames
;
register struct passwd
*pw
;
register struct dirent
*dirp
;
if (looking_for_lognames
) {
if ((pw
= getpwent()) == NULL
)
return (str2short(pw
->pw_name
));
if (dirp
= readdir(dir_fd
))
return (str2short(dirp
->d_name
));
for (i
= 0; items
[i
]; i
++)
#define FREE_ITEMS(items) { \
omask = sigblock(sigmask(SIGINT));\
(void) sigsetmask(omask);\
* Perform a RECOGNIZE or LIST command on string "word".
tsearch(word
, command
, max_word_length
)
static Char
**items
= NULL
;
register numitems
= 0, ignoring
= TRUE
, nignored
= 0;
register name_length
, looking_for_lognames
;
Char tilded_dir
[MAXPATHLEN
+ 1], dir
[MAXPATHLEN
+ 1];
Char name
[MAXNAMLEN
+ 1], extended_name
[MAXNAMLEN
+ 1];
looking_for_lognames
= (*word
== '~') && (Strchr(word
, '/') == NULL
);
if (looking_for_lognames
) {
copyn(name
, &word
[1], MAXNAMLEN
); /* name sans ~ */
extract_dir_and_name(word
, dir
, name
);
if (tilde(tilded_dir
, dir
) == 0)
dir_fd
= opendir(*tilded_dir
? short2str(tilded_dir
) : ".");
again
: /* search for matches */
name_length
= Strlen(name
);
for (numitems
= 0; entry
= getentry(dir_fd
, looking_for_lognames
);) {
if (!is_prefix(name
, entry
))
/* Don't match . files on null prefix match */
if (name_length
== 0 && entry
[0] == '.' &&
if (numitems
>= MAXITEMS
) {
xprintf("\nYikes!! Too many %s!!\n",
"names in password file" : "files");
items
= (Char
**) xcalloc(sizeof(items
[0]), MAXITEMS
);
items
[numitems
] = (Char
*) xmalloc((size_t) (Strlen(entry
) + 1) *
copyn(items
[numitems
], entry
, MAXNAMLEN
);
else { /* RECOGNIZE command */
if (ignoring
&& ignored(entry
))
else if (recognize(extended_name
,
entry
, name_length
, ++numitems
))
if (ignoring
&& numitems
== 0 && nignored
> 0) {
if (looking_for_lognames
)
if (looking_for_lognames
)
if (command
== RECOGNIZE
) {
if (looking_for_lognames
)
copyn(word
, STRtilde
, 1);
copyn(word
, dir
, max_word_length
);
catn(word
, extended_name
, max_word_length
);
qsort((ptr_t
) items
, numitems
, sizeof(items
[0]),
(int (*)(const void *, const void *)) sortscmp
);
print_by_column(looking_for_lognames
? NULL
: tilded_dir
,
* Object: extend what user typed up to an ambiguity.
* On first match, copy full entry (assume it'll be the only match)
* On subsequent matches, shorten extended_name to the first
* Character mismatch between extended_name and entry.
* If we shorten it back to the prefix length, stop searching.
recognize(extended_name
, entry
, name_length
, numitems
)
Char
*extended_name
, *entry
;
int name_length
, numitems
;
if (numitems
== 1) /* 1st match */
copyn(extended_name
, entry
, MAXNAMLEN
);
else { /* 2nd & subsequent matches */
for (ent
= entry
; *x
&& *x
== *ent
++; x
++, len
++);
*x
= '\0'; /* Shorten at 1st Char diff */
if (len
== name_length
) /* Ambiguous to prefix? */
return (-1); /* So stop now and save time */
* Return true if check matches initial Chars in template.
* This differs from PWB imatch in that if check is null
is_prefix(check
, template)
register Char
*check
, *template;
while (*check
++ == *template++);
* Return true if the Chars in template appear at the
* end of check, I.e., are it's suffix.
is_suffix(check
, template)
for (t
= template; *t
++;);
if (c
== check
|| *--t
!= *--c
)
tenex(inputline
, inputline_size
)
register int numitems
, num_read
;
while ((num_read
= read(SHIN
, tinputline
, BUFSIZ
)) > 0) {
static Char delims
[] = {' ', '\'', '"', '\t', ';', '&', '<',
'>', '(', ')', '|', '^', '%', '\0'};
register Char
*str_end
, *word_start
, last_Char
, should_retype
;
for (i
= 0; i
< num_read
; i
++)
inputline
[i
] = (unsigned char) tinputline
[i
];
last_Char
= inputline
[num_read
- 1] & ASCII
;
if (last_Char
== '\n' || num_read
== inputline_size
)
command
= (last_Char
== ESC
) ? RECOGNIZE
: LIST
;
str_end
= &inputline
[num_read
];
--str_end
; /* wipeout trailing cmd Char */
* Find LAST occurence of a delimiter in the inputline. The word start
* is one Character past it.
for (word_start
= str_end
; word_start
> inputline
; --word_start
)
if (Strchr(delims
, word_start
[-1]))
space_left
= inputline_size
- (word_start
- inputline
) - 1;
numitems
= tsearch(word_start
, command
, space_left
);
if (command
== RECOGNIZE
) {
/* print from str_end on */
print_recognized_stuff(str_end
);
if (numitems
!= 1) /* Beep = No match/ambiguous */
* Tabs in the input line cause trouble after a pushback. tty driver
* won't backspace over them because column positions are now
* incorrect. This is solved by retyping over current line.
if (Strchr(inputline
, '\t')) { /* tab Char in input line? */
if (command
== LIST
) /* Always retype after a LIST */
if ((vp
= adrof(STRfignore
)) == NULL
|| (cp
= vp
->vec
) == NULL
)
for (; *cp
!= NULL
; cp
++)
if (is_suffix(entry
, *cp
))