* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley Software License Agreement
* specifies the terms and conditions for redistribution.
static char *sccsid
= "@(#)file.c 5.6 (Berkeley) %G%";
* 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.
typedef enum {LIST
, RECOGNIZE
} COMMAND
;
int sortscmp(); /* defined in sh.glob.c */
* 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 tchars tchars
; /* INT, QUIT, XON, XOFF, EOF, BRK */
(void) ioctl(SHIN
, TIOCGETC
, (char *)&tchars
);
(void) ioctl(SHIN
, TIOCSETC
, (char *)&tchars
);
* This must be done after every command: if
* the tty gets into raw or cbreak mode the user
* can't even type 'reset'.
(void) ioctl(SHIN
, TIOCGETP
, (char *)&sgtty
);
if (sgtty
.sg_flags
& (RAW
|CBREAK
)) {
sgtty
.sg_flags
&= ~(RAW
|CBREAK
);
(void) ioctl(SHIN
, TIOCSETP
, (char *)&sgtty
);
(void) ioctl(SHIN
, TIOCSETC
, (char *)&tchars
);
* Move back to beginning of current line
struct sgttyb tty
, tty_normal
;
omask
= sigblock(sigmask(SIGINT
));
(void) ioctl(SHIN
, TIOCGETP
, (char *)&tty
);
(void) ioctl(SHIN
, TIOCSETN
, (char *)&tty
);
(void) write(SHOUT
, "\r", 1);
(void) ioctl(SHIN
, TIOCSETN
, (char *)&tty_normal
);
(void) sigsetmask(omask
);
* Push string contents back into tty queue
struct sgttyb tty
, tty_normal
;
omask
= sigblock(sigmask(SIGINT
));
(void) ioctl(SHOUT
, TIOCGETP
, (char *)&tty
);
(void) ioctl(SHOUT
, TIOCSETN
, (char *)&tty
);
for (p
= string
; *p
; p
++)
(void) ioctl(SHOUT
, TIOCSTI
, p
);
(void) ioctl(SHOUT
, TIOCSETN
, (char *)&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
);
if (lstat(path
, &statb
) == 0) {
switch(statb
.st_mode
& S_IFMT
) {
if (stat(path
, &statb
) == 0 && /* follow it out */
(statb
.st_mode
& S_IFMT
) == S_IFDIR
)
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
, (char *)&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
++) {
putchar(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("home"));
(void) strcpy(new, pw
->pw_dir
);
* Cause pending line to be printed
int pending_input
= LPENDIN
;
(void) ioctl(SHOUT
, TIOCLBIS
, (char *)&pending_input
);
if (adrof("nobeep") == 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: ^[ */
printf("\210\210 \210\210");
case 1: /* overstrike the ^, erase the [ */
printf("\210\210%s \210", recognized_part
);
default: /* overstrike both characters ^[ */
printf("\210\210%s", 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
)
register struct passwd
*pw
;
register struct direct
*dirp
;
if (looking_for_lognames
) {
if ((pw
= getpwent()) == NULL
)
if (dirp
= readdir(dir_fd
))
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".
search(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
== '~') && (index(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
? 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
) {
printf ("\nYikes!! Too many %s!!\n",
"names in password file":"files");
items
= (char **) calloc(sizeof (items
[1]),
items
[numitems
] = xalloc((unsigned)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
, dir
, max_word_length
);
catn(word
, extended_name
, max_word_length
);
qsort((char *)items
, numitems
, sizeof(items
[1]), 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
;
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
, inputline
, inputline_size
)) > 0) {
static char *delims
= " '\"\t;&<>()|^%";
register char *str_end
, *word_start
, last_char
, should_retype
;
last_char
= inputline
[num_read
- 1] & 0177;
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 (index(delims
, word_start
[-1]))
space_left
= inputline_size
- (word_start
- inputline
) - 1;
numitems
= search(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
if (index(inputline
, '\t')) { /* tab char in input line? */
if (command
== LIST
) /* Always retype after a LIST */
if ((vp
= adrof("fignore")) == NULL
|| (cp
= vp
->vec
) == NULL
)
for (; *cp
!= NULL
; cp
++)
if (is_suffix(entry
, *cp
))