#ifdef FILEC
#include "sh.h"
#include <sys/types.h>
#include <dirent.h>
#include <pwd.h>
#include "sh.tconst.h"
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
#define ESC '\033'
extern DIR *opendir_(tchar *);
static char *BELL = "\07";
static char *CTRLR = "^R\n";
typedef enum {LIST, RECOGNIZE} COMMAND;
static jmp_buf osetexit;
static struct termios tty_save;
static struct termios tty_new;
static int is_prefix(tchar *, tchar *);
static int is_suffix(tchar *, tchar *);
static int ignored(tchar *);
bool filec = 0;
static void
setup_tty(int on)
{
int omask;
#ifdef TRACE
tprintf("TRACE- setup_tty()\n");
#endif
omask = sigblock(sigmask(SIGINT));
if (on) {
(void) ioctl(SHIN, TCGETS, (char *)&tty_save);
getexit(osetexit);
if (setjmp(reslab)) {
(void) ioctl(SHIN, TCSETSW, (char *)&tty_save);
resexit(osetexit);
reset();
}
tty_new = tty_save;
tty_new.c_cc[VEOL] = ESC;
tty_new.c_iflag |= IMAXBEL | BRKINT | IGNPAR;
tty_new.c_lflag |= ICANON;
tty_new.c_lflag |= ECHOCTL;
tty_new.c_oflag &= ~OCRNL;
(void) ioctl(SHIN, TCSETSW, (char *)&tty_new);
} else {
(void) ioctl(SHIN, TCSETSW, (char *)&tty_save);
resexit(osetexit);
}
(void) sigsetmask(omask);
}
static void
termchars(void)
{
extern char *tgetstr();
char bp[1024];
static char area[256];
static int been_here = 0;
char *ap = area;
char *s;
char *term;
#ifdef TRACE
tprintf("TRACE- termchars()\n");
#endif
if (been_here)
return;
been_here = TRUE;
if ((term = getenv("TERM")) == NULL)
return;
if (tgetent(bp, term) != 1)
return;
if (s = tgetstr("vb", &ap))
BELL = s;
}
static void
back_to_col_1(void)
{
int omask;
#ifdef TRACE
tprintf("TRACE- back_to_col_1()\n");
#endif
omask = sigblock(sigmask(SIGINT));
(void) write(SHOUT, "\r", 1);
(void) sigsetmask(omask);
}
static void
pushback(tchar *string, int echoflag)
{
tchar *p;
struct termios tty;
int omask, retry = 0;
#ifdef TRACE
tprintf("TRACE- pushback()\n");
#endif
omask = sigblock(sigmask(SIGINT));
tty = tty_new;
if (!echoflag)
tty.c_lflag &= ~ECHO;
again:
(void) ioctl(SHIN, TCSETSF, (char *)&tty);
for (p = string; *p; p++) {
char mbc[MB_LEN_MAX];
int i, j = wctomb(mbc, (wchar_t)*p);
if (j < 0) {
continue;
}
for (i = 0; i < j; ++i) {
if (ioctl(SHIN, TIOCSTI, mbc + i) != 0 &&
errno == EAGAIN) {
if (retry++ < 5)
goto again;
}
}
}
if (tty.c_lflag != tty_new.c_lflag)
(void) ioctl(SHIN, TCSETS, (char *)&tty_new);
(void) sigsetmask(omask);
}
void
catn(tchar *des, tchar *src, int count)
{
#ifdef TRACE
tprintf("TRACE- catn()\n");
#endif
while (--count >= 0 && *des)
des++;
while (--count >= 0)
if ((*des++ = *src++) == '\0')
return;
*des = '\0';
}
static int
max(a, b)
{
return (a > b ? a : b);
}
void
copyn(tchar *des, tchar *src, int count)
{
#ifdef TRACE
tprintf("TRACE- copyn()\n");
#endif
while (--count >= 0)
if ((*des++ = *src++) == '\0')
return;
*des = '\0';
}
static int
fcompare(tchar **file1, tchar **file2)
{
#ifdef TRACE
tprintf("TRACE- fcompare()\n");
#endif
return (strcoll_(*file1, *file2));
}
static char
filetype(tchar *dir, tchar *file, int nosym)
{
tchar path[MAXPATHLEN + 1];
struct stat statb;
#ifdef TRACE
tprintf("TRACE- filetype()\n");
#endif
if (dir) {
catn(strcpy_(path, dir), file, MAXPATHLEN);
if (nosym) {
if (stat_(path, &statb) < 0)
return (' ');
} else {
if (lstat_(path, &statb) < 0)
return (' ');
}
if ((statb.st_mode & S_IFMT) == S_IFLNK)
return ('@');
if ((statb.st_mode & S_IFMT) == S_IFDIR)
return ('/');
if (((statb.st_mode & S_IFMT) == S_IFREG) &&
(statb.st_mode & 011))
return ('*');
}
return (' ');
}
static void
print_by_column(tchar *dir, tchar *items[], int count, int looking_for_command)
{
int i, rows, r, c, maxwidth = 0, columns;
#ifdef TRACE
tprintf("TRACE- print_by_column()\n");
#endif
for (i = 0; i < count; i++)
maxwidth = max(maxwidth, tswidth(items[i]));
maxwidth += looking_for_command ? 1 : 2;
columns = max(78 / maxwidth, 1);
rows = (count + (columns - 1)) / columns;
for (r = 0; r < rows; r++) {
for (c = 0; c < columns; c++) {
i = c * rows + r;
if (i < count) {
int w;
printf("%t", items[i]);
w = tswidth(items[i]);
if (!looking_for_command) {
printf("%c",
(tchar) filetype(dir, items[i], 0));
w++;
}
if (c < columns - 1)
for (; w < maxwidth; w++)
printf(" ");
}
}
printf("\n");
}
}
tchar *
tilde(tchar *new, tchar *old)
{
tchar *o, *p;
struct passwd *pw;
static tchar person[40];
char person_[40];
tchar *pw_dir;
#ifdef TRACE
tprintf("TRACE- tilde()\n");
#endif
if (old[0] != '~')
return (strcpy_(new, old));
for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
;
*p = '\0';
if (person[0] == '\0')
(void) strcpy_(new, value(S_home ));
else {
pw = getpwnam(tstostr(person_, person));
if (pw == NULL)
return (NULL);
pw_dir = strtots((tchar *)NULL, pw->pw_dir);
(void) strcpy_(new, pw_dir);
xfree(pw_dir);
}
(void) strcat_(new, o);
return (new);
}
static void
sim_retype(void)
{
#ifdef notdef
struct termios tty_pending;
#ifdef TRACE
tprintf("TRACE- sim_retypr()\n");
#endif
tty_pending = tty_new;
tty_pending.c_lflag |= PENDIN;
(void) ioctl(SHIN, TCSETS, (char *)&tty_pending);
#else
#ifdef TRACE
tprintf("TRACE- sim_retype()\n");
#endif
(void) write(SHOUT, CTRLR, strlen(CTRLR));
printprompt();
#endif
}
static int
beep_outc(int c)
{
char buf[1];
buf[0] = c;
(void) write(SHOUT, buf, 1);
return 0;
}
static void
beep(void)
{
#ifdef TRACE
tprintf("TRACE- beep()\n");
#endif
if (adrof(S_nobeep ) == 0)
(void) tputs(BELL, 0, beep_outc);
}
static void
print_recognized_stuff(tchar *recognized_part)
{
int unit = didfds ? 1 : SHOUT;
#ifdef TRACE
tprintf("TRACE- print_recognized_stuff()\n");
#endif
flush();
switch (tswidth(recognized_part)) {
case 0:
write(unit, "\b\b \b\b", sizeof "\b\b \b\b" - 1);
break;
case 1:
write(unit, "\b\b", 2);
printf("%t", recognized_part);
write(unit, " \b\b", 4);
break;
default:
write(unit, "\b\b", 2);
printf("%t", recognized_part);
break;
}
flush();
}
static void
extract_dir_and_name(tchar *path, tchar *dir, tchar *name)
{
tchar *p;
#ifdef TRACE
tprintf("TRACE- extract_dir_and_name()\n");
#endif
p = rindex_(path, '/');
if (p == NOSTR) {
copyn(name, path, MAXNAMLEN);
dir[0] = '\0';
} else {
copyn(name, ++p, MAXNAMLEN);
copyn(dir, path, p - path);
}
}
tchar *
getentry(DIR *dir_fd, int looking_for_lognames)
{
struct passwd *pw;
struct dirent *dirp;
static tchar strbuf[MAXNAMLEN+1];
#ifdef TRACE
tprintf("TRACE- getentry()\n");
#endif
if (looking_for_lognames) {
if ((pw = getpwent()) == NULL)
return (NULL);
return (strtots(strbuf, pw->pw_name));
}
if (dirp = readdir(dir_fd))
return (strtots(strbuf, dirp->d_name));
return (NULL);
}
static void
free_items(tchar **items)
{
int i;
#ifdef TRACE
tprintf("TRACE- free_items()\n");
#endif
for (i = 0; items[i]; i++)
xfree(items[i]);
xfree((char *)items);
}
#define FREE_ITEMS(items) { \
int omask;\
\
omask = sigblock(sigmask(SIGINT));\
free_items(items);\
items = NULL;\
(void) sigsetmask(omask);\
}
static int
search2(tchar *word, COMMAND command, int max_word_length)
{
static tchar **items = NULL;
DIR *dir_fd;
int numitems = 0, ignoring = TRUE, nignored = 0;
int name_length, looking_for_lognames;
tchar tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
tchar name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1];
tchar *entry;
#define MAXITEMS 1024
#ifdef TRACE
tprintf("TRACE- search2()\n");
#endif
if (items != NULL)
FREE_ITEMS(items);
looking_for_lognames = (*word == '~') && (index_(word, '/') == NULL);
if (looking_for_lognames) {
(void) setpwent();
copyn(name, &word[1], MAXNAMLEN);
} else {
extract_dir_and_name(word, dir, name);
if (tilde(tilded_dir, dir) == 0)
return (0);
dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT );
if (dir_fd == NULL)
return (0);
}
again:
name_length = strlen_(name);
for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) {
if (!is_prefix(name, entry))
continue;
if (name_length == 0 && entry[0] == '.' &&
!looking_for_lognames)
continue;
if (command == LIST) {
if (numitems >= MAXITEMS) {
printf("\nYikes!! Too many %s!!\n",
looking_for_lognames ?
"names in password file":"files");
break;
}
if (items == NULL)
items = (tchar **)xcalloc(sizeof (items[1]),
MAXITEMS+1);
items[numitems] = (tchar *)xalloc((unsigned)(strlen_(entry) + 1) * sizeof (tchar));
copyn(items[numitems], entry, MAXNAMLEN);
numitems++;
} else {
if (ignoring && ignored(entry))
nignored++;
else if (recognize(extended_name,
entry, name_length, ++numitems))
break;
}
}
if (ignoring && numitems == 0 && nignored > 0) {
ignoring = FALSE;
nignored = 0;
if (looking_for_lognames)
(void) setpwent();
else
rewinddir(dir_fd);
goto again;
}
if (looking_for_lognames)
(void) endpwent();
else {
unsetfd(dir_fd->dd_fd);
closedir_(dir_fd);
}
if (command == RECOGNIZE && numitems > 0) {
if (looking_for_lognames)
copyn(word, S_TIL , 1);
else
copyn(word, dir, max_word_length);
catn(word, extended_name, max_word_length);
return (numitems);
}
if (command == LIST) {
qsort((char *)items, numitems, sizeof (items[1]),
(int (*)(const void *, const void *))fcompare);
print_by_column(looking_for_lognames ? NULL : tilded_dir,
items, numitems, 0);
if (items != NULL)
FREE_ITEMS(items);
}
return (0);
}
int
recognize(tchar *extended_name, tchar *entry, int name_length, int numitems)
{
#ifdef TRACE
tprintf("TRACE- recognize()\n");
#endif
if (numitems == 1)
copyn(extended_name, entry, MAXNAMLEN);
else {
tchar *x, *ent;
int len = 0;
x = extended_name;
for (ent = entry; *x && *x == *ent++; x++, len++)
;
*x = '\0';
if (len == name_length)
return (-1);
}
return (0);
}
static int
is_prefix(tchar *check, tchar *template)
{
#ifdef TRACE
tprintf("TRACE- is_prefix()\n");
#endif
do
if (*check == 0)
return (TRUE);
while (*check++ == *template++);
return (FALSE);
}
static int
is_suffix(tchar *check, tchar *template)
{
tchar *c, *t;
#ifdef TRACE
tprintf("TRACE- is_suffix()\n");
#endif
for (c = check; *c++; )
;
for (t = template; *t++; )
;
for (;;) {
if (t == template)
return (TRUE);
if (c == check || *--t != *--c)
return (FALSE);
}
}
int
tenex(tchar *inputline, int inputline_size)
{
int numitems, num_read, should_retype;
int i;
#ifdef TRACE
tprintf("TRACE- tenex()\n");
#endif
setup_tty(ON);
termchars();
num_read = 0;
should_retype = FALSE;
while ((i = read_(SHIN, inputline+num_read, inputline_size-num_read))
> 0) {
static tchar *delims = S_DELIM ;
tchar *str_end, *word_start, last_char;
int space_left;
struct termios tty;
COMMAND command;
num_read += i;
inputline[num_read] = '\0';
last_char = inputline[num_read - 1] & TRIM;
if ((num_read >= inputline_size) || (last_char == '\n'))
break;
str_end = &inputline[num_read];
if (last_char == ESC) {
command = RECOGNIZE;
*--str_end = '\0';
} else
command = LIST;
tty = tty_new;
tty.c_lflag &= ~ECHO;
(void) ioctl(SHIN, TCSETSF, (char *)&tty);
if (command == LIST)
printf("\n");
for (word_start = str_end; word_start > inputline;
--word_start) {
if (index_(delims, word_start[-1]) ||
isauxsp(word_start[-1]))
break;
}
space_left = inputline_size - (word_start - inputline) - 1;
numitems = search2(word_start, command, space_left);
if (index_(inputline, '\t')) {
back_to_col_1();
should_retype = TRUE;
}
if (command == LIST)
should_retype = TRUE;
if (should_retype)
printprompt();
pushback(inputline, should_retype);
num_read = 0;
should_retype = FALSE;
if (command == RECOGNIZE) {
print_recognized_stuff(str_end);
if (numitems != 1)
beep();
}
}
setup_tty(OFF);
return (num_read);
}
static int
ignored(tchar *entry)
{
struct varent *vp;
tchar **cp;
#ifdef TRACE
tprintf("TRACE- ignored()\n");
#endif
if ((vp = adrof(S_fignore )) == NULL ||
(cp = vp->vec) == NULL)
return (FALSE);
for (; *cp != NULL; cp++)
if (is_suffix(entry, *cp))
return (TRUE);
return (FALSE);
}
#endif