1
0
Files
irix-657m-src/eoe/cmd/csh/sh.file.c
2022-09-29 17:59:04 +03:00

832 lines
18 KiB
C

/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
/* UNIX System Laboratories, Inc. */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#ident "$Revision: 2.13 $"
/*******************************************************************
PROPRIETARY NOTICE (Combined)
This source code is unpublished proprietary information
constituting, or derived under license from AT&T's UNIX(r) System V.
In addition, portions of such source code were derived from Berkeley
4.3 BSD under license from the Regents of the University of
California.
Copyright Notice
Notice of copyright on this source code product does not indicate
publication.
(c) 1986,1987,1988,1989 Sun Microsystems, Inc
(c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
All rights reserved.
********************************************************************/
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley Software License Agreement
* specifies the terms and conditions for redistribution.
*/
/*
* Tenex style file name recognition, .. and more.
* History:
* Author: Ken Greer, Sept. 1975, CMU.
* Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
*/
#include <sys/types.h>
#include <widec.h>
#include <pwd.h>
#include <unistd.h>
#include <pfmt.h>
#include "sh.h"
#include "sh.wconst.h"
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
#define ESC '\033'
#define CNTL_D '\004'
static char *BELL = "\07";
typedef enum {LIST, RECOGNIZE} COMMAND;
static jmp_buf osetexit; /* saved setexit() state */
static struct termio tty_save; /* saved terminal state */
static struct termio tty_new; /* new terminal state */
static void catn(wchar_t *, wchar_t *, int);
static void copyn(wchar_t *, wchar_t *, int);
static wchar_t *getentry(wchar_t *, DIR *, int, int *);
static bool ignored(wchar_t *);
static int is_prefix(wchar_t *, wchar_t *);
static int recognize(wchar_t *, wchar_t *, int, int);
static wchar_t *tilde(wchar_t *, wchar_t *);
extern int tgetent(char *, char *);
extern char *tgetstr(char *, char **);
static void
update_tty(struct termio *tp)
{
struct termio tty_cur;
#ifdef TRACE
tprintf("TRACE- update_tty()\n");
#endif
(void) ioctl(SHIN, TCGETA, (char *)&tty_cur);
if (tty_cur.c_lflag & FLUSHO)
tp->c_lflag |= FLUSHO;
else
tp->c_lflag &= ~FLUSHO;
}
static int
termchars(void)
{
char bp[1024];
static char area[256];
static int been_here = 0;
char *ap = area;
register 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)) /* Visible Bell */
BELL = s;
}
/*
* Move back to beginning of current line
*/
static int
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);
}
/*
* Push string contents back into tty queue
*/
static int
pushback(wchar_t *string, int echoflag)
{
register wchar_t *p;
register int n, i;
int omask;
struct termio tty;
char chbuf[MB_LEN_MAX];
#ifdef TRACE
tprintf("TRACE- pushback()\n");
#endif
omask = sigblock(sigmask(SIGINT));
tty = tty_new;
if( !echoflag)
tty.c_lflag &= ~ECHO;
(void)ioctl(SHIN, TCSETAF, (char *)&tty);
for(p = string; *p; p++) {
n = wctomb(chbuf, *p);
if(n < 0)
continue; /* error, but what else ? */
/*DDDD
for(i = n; i >= 0; i--)
*/
for(i = 0; i < n; i++)
(void)ioctl(SHIN, TIOCSTI, chbuf + i);
}
if(tty.c_lflag != tty_new.c_lflag)
(void)ioctl(SHIN, TCSETA, (char *)&tty_new);
(void)sigsetmask(omask);
}
/*
* Concatenate src onto tail of des.
* Des is a string whose maximum length is count.
* Always null terminate.
*/
static void
catn(wchar_t *des, wchar_t *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(int a, int b)
{
return (a > b ? a : b);
}
/*
* Like strncpy but always leave room for trailing \0
* and always null terminate.
*/
static void
copyn(wchar_t *des, wchar_t *src, int count)
{
#ifdef TRACE
tprintf("TRACE- copyn()\n");
#endif
while(--count >= 0)
if((*des++ = *src++) == '\0')
return;
*des = '\0';
}
/*
* For qsort()
*/
static int
fcompare(const void *file1, const void *file2)
{
#ifdef TRACE
tprintf("TRACE- fcompare()\n");
#endif
return(wscmp(*((wchar_t**)file1), *((wchar_t**)file2)));
}
static wchar_t
filetype(wchar_t *dir, wchar_t *file, int nosym)
{
struct stat statb;
wchar_t path[MAXPATHLEN + 1];
#ifdef TRACE
tprintf("TRACE- filetype()\n");
#endif
if(dir) {
catn(wscpy(path, dir), file, MAXPATHLEN);
if (nosym) {
if (stat_(path, &statb) < 0)
return (' ');
} else {
if (lstat_(path, &statb) < 0)
return (' ');
}
switch(statb.st_mode & S_IFMT) {
case S_IFDIR:
return ('/');
case S_IFLNK:
if (stat_(path, &statb) == 0 && /* follow it out */
(statb.st_mode & S_IFMT) == S_IFDIR)
return ('>');
else
return ('@');
case S_IFSOCK:
return ('=');
default:
if (statb.st_mode & 0111)
return ('*');
}
}
return (' ');
}
/*
* Print sorted down columns
*/
static int
print_by_column(wchar_t *dir, wchar_t *items[], int count, int lookforcmd)
{
register 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]));
/* for the file tag and space */
maxwidth += lookforcmd? 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) {
register int w;
shprintf("%t", items[i]);
w = tswidth(items[i]);
/*
* Print filename followed by
* '@' or '/' or '*' or ' '
*/
if( !lookforcmd) {
shprintf("%T", filetype(dir, items[i], 0));
w++;
}
if (c < columns - 1) /* last column? */
for (; w < maxwidth; w++)
shprintf(" ");
}
}
shprintf("\n");
}
}
/*
* Expand file name with possible tilde usage
* ~person/mumble
* expands to
* home_directory_of_person/mumble
*
* ret: NULL if bad name or mbchar
*/
static wchar_t *person;
static wchar_t *
tilde(wchar_t *new, wchar_t *old)
{
register wchar_t *p;
register wchar_t *d;
register char *s;
register struct passwd *pw;
int cflag;
char cperson[CSHBUFSIZ];
if (person == NULL) {
person = (wchar_t *)calloc(1, sizeof (wchar_t) * CSHBUFSIZ);
}
#ifdef TRACE
tprintf("TRACE- tilde()\n");
#endif
if(old[0] != '~')
return(wscpy(new, old)); /* no ~ */
/*
* scan for path delimiter
* XXXX multibyte ?
*/
for(p = person, old++; *old && (*old != '/'); *p++ = *old++);
*p = '\0';
if(person[0] == '\0')
wscpy(new, value(S_home)); /* only ~/mumble */
else {
s = tstostr(cperson, person, &cflag);
if(cflag)
return(NULL); /* illegal wchar = unknown */
if( ! (pw = getpwnam(s)))
return(NULL); /* bad login name */
d = strtots(NOSTR, pw->pw_dir, &cflag);
if(cflag)
return(NULL); /* bad chars = unknown user */
wscpy(new, d);
xfree(d); /* free it */
}
wscat(new, old); /* add mumble */
return(new);
}
static int
beep(void)
{
#ifdef TRACE
tprintf("TRACE- beep()\n");
#endif
if (adrof(S_nobeep /*"nobeep" */) == 0)
(void) write(SHOUT, BELL, strlen(BELL));
}
/*
* Erase that silly ^[ (if necessary) and print the recognized
* part of the string.
*/
static void
precstuff(wchar_t *recpart)
{
int unit;
#ifdef TRACE
tprintf("TRACE- precstuff()\n");
#endif
unit = didfds? 1 : SHOUT;
flush();
if (!(tty_save.c_lflag & ECHOCTL)) { /* no ^[ was echoed */
shprintf("%t", recpart);
flush();
return;
}
switch(tswidth(recpart)) {
/*
* erase two characters: ^[
*/
case 0:
write(unit, "\010\010 \010\010", 6);
break;
/*
* overstrike the ^, erase the [
*/
case 1:
write(unit, "\010\010", 2);
shprintf("%t", recpart);
write(unit, " \010\010", 4);
break;
/*
* overstrike both characters ^[
*/
default:
write(unit, "\010\010", 2);
shprintf("%t", recpart);
break;
}
flush();
}
/*
* Parse full path in file into 2 parts: directory and file names
* Should leave final slash (/) at end of dir.
*/
static
extract_dir_and_name(wchar_t *path, wchar_t *dir, wchar_t *name)
{
register wchar_t *p;
#ifdef TRACE
tprintf("TRACE- extract_dir_and_name()\n");
#endif
p = wsrchr(path, '/');
if (p == NOSTR) {
copyn(name, path, MAXNAMLEN);
dir[0] = '\0';
} else {
copyn(name, ++p, MAXNAMLEN);
copyn(dir, path, p - path);
}
}
static wchar_t *
getentry(wchar_t *wcp, DIR *dir_fd, int looklog, int *cflag)
{
register struct passwd *pw;
register struct dirent *dirp;
#ifdef TRACE
tprintf("TRACE- getentry()\n");
#endif
if(looklog) {
if( !(pw = getpwent()))
return(NULL);
return(strtots(wcp, pw->pw_name, cflag));
}
if( !(dirp = readdir(dir_fd)))
return(NULL);
strtots(wcp, dirp->d_name, cflag);
if(*cflag)
err_fntruncated(dirp->d_name);
return(wcp);
}
static int
free_items(wchar_t **items)
{
register int i;
#ifdef TRACE
tprintf("TRACE- free_items()\n");
#endif
for(i = 0; items[i]; i++)
free(items[i]);
free((char *)items);
}
#define FREE_ITEMS(items) { \
int omask;\
\
omask = sigblock(sigmask(SIGINT));\
free_items(items);\
items = NULL;\
(void) sigsetmask(omask);\
}
/*
* Perform a RECOGNIZE or LIST command on string "word".
*/
static wchar_t **items = NULL;
static int
search2(wchar_t *word, COMMAND command, int max_word_length)
{
register DIR *dir_fd;
register nitems = 0, ignoring = TRUE, nignored = 0;
register name_length, looklog;
wchar_t *entry;
int cflag;
wchar_t tilded_dir[MAXPATHLEN + 1];
wchar_t dir[MAXPATHLEN + 1];
wchar_t name[MAXNAMLEN + 1];
wchar_t extndname[MAXNAMLEN + 1];
wchar_t ename[MAXNAMLEN + 1];
#define MAXITEMS 1024
#ifdef TRACE
tprintf("TRACE- search2()\n");
#endif
if(items)
FREE_ITEMS(items);
looklog = (*word == '~') && !wschr(word, '/');
if(looklog) {
(void)setpwent();
copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */
} else {
extract_dir_and_name(word, dir, name);
if( !tilde(tilded_dir, dir))
return(0);
dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT);
if( !dir_fd)
return(0);
}
/*
* search loop
*/
again:
name_length = wslen(name);
for(nitems = 0; entry = getentry(ename, dir_fd, looklog, &cflag); ) {
if(cflag && looklog)
continue; /* ignore wrong logname */
if( !is_prefix(name, entry))
continue;
/*
* Don't match . files on null prefix match
*/
if( !name_length && (entry[0] == '.') && !looklog)
continue;
if(command == LIST) {
if(nitems >= MAXITEMS - 1) {
showstr(MM_ERROR,
looklog?
gettxt(_SGI_DMMX_csh_2manynames,
"\nYikes! Too many names in passwd!")
:
gettxt(_SGI_DMMX_csh_2manyfiles,
"\nToo many files: not all will be listed"), 0);
break;
}
if( !items)
items = (wchar_t **)salloc(MAXITEMS, sizeof(wchar_t **));
items[nitems] = wcalloc(wslen(entry) + 1);
copyn(items[nitems], entry, MAXNAMLEN);
nitems++;
} else { /* RECOGNIZE command */
if(ignoring && ignored(entry))
nignored++;
else {
if(recognize(extndname, entry, name_length, ++nitems))
break;
}
}
}
if(ignoring && !nitems && (nignored > 0)) {
ignoring = FALSE;
nignored = 0;
if(looklog)
(void)setpwent();
else
rewinddir(dir_fd);
goto again;
}
if(looklog)
(void) endpwent(); /* close passwd database */
else
closedir(dir_fd);
if(command == RECOGNIZE && nitems > 0) {
if(looklog)
copyn(word, S_TIL, 1);
else
copyn(word, dir, max_word_length); /* put back dir part */
catn(word, extndname, max_word_length); /* add extended name */
return(nitems);
}
if(command == LIST) {
qsort( (wchar_t *)items, nitems, sizeof(items[1]), fcompare);
/*
* Never looking for commands in this version, so final
* argument forced to 0. If command name completion is
* reinstated, this must change.
*/
print_by_column(looklog ? NULL : tilded_dir,
items, nitems, 0);
if(items)
FREE_ITEMS(items);
}
return(0);
}
/*
* Object: extend what user typed up to an ambiguity.
* Algorithm:
* On first match, copy full entry (assume it'll be the only match)
* On subsequent matches, shorten extndname to the first
* character mismatch between extndname and entry.
* If we shorten it back to the prefix length, stop searching.
*/
static int
recognize(wchar_t *extndname, wchar_t *entry, int name_length, int nitems)
{
#ifdef TRACE
tprintf("TRACE- recognize()\n");
#endif
if (nitems == 1) /* 1st match */
copyn(extndname, entry, MAXNAMLEN);
else { /* 2nd and subsequent matches */
register wchar_t *x, *ent;
register int len = 0;
x = extndname;
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 (0);
}
/*
* Return true if check items initial chars in template
* This differs from PWB imatch in that if check is null
* it items anything
*/
static int
is_prefix(wchar_t *check, wchar_t *template)
{
#ifdef TRACE
tprintf("TRACE- is_prefix()\n");
#endif
do {
if (*check == 0)
return (TRUE);
} while (*check++ == *template++);
return (FALSE);
}
/*
* Return true if the chars in template appear at the
* end of check, i.e., are its suffix.
*/
static int
is_suffix(wchar_t *check, wchar_t *template)
{
register wchar_t *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(wchar_t *iline, int ilsize)
{
register int nitems, nread, should_retype;
int i;
int omask;
#ifdef TRACE
tprintf("TRACE- tenex()\n");
#endif
/*
* START of tty initialization block that was setup_tty(ON)
*
* The shell makes sure that the tty is not in some weird state
* and fixes it if it is. But it should be noted that the
* tenex routine will not work correctly in CBREAK or RAW mode
* so this code below is, therefore, mandatory.
*
* have to do this all inline, or we violate requirements
* for setjmp, since we would longjmp back into a function
* that has already returned... We've been lucky about this
* in the past, but got bit by it in ficus...
*
* Also, in order to recognize the ESC (filename-completion)
* character, set EOL to ESC. This way, ESC will terminate
* the line, but still be in the input stream.
* EOT (filename list) will also terminate the line,
* but will not appear in the input stream.
*/
omask = sigblock(sigmask(SIGINT));
(void) ioctl(SHIN, TCGETA, (char *)&tty_save);
getexit(osetexit);
if (setjmp(reslab)) {
update_tty(&tty_save);
(void) ioctl(SHIN, TCSETAW, (char *)&tty_save);
resexit(osetexit);
reset();
}
else {
tty_new = tty_save;
tty_new.c_cc[VEOL] = ESC;
#define CANONMODE (ICANON|ECHO)
if ((tty_new.c_lflag & CANONMODE) != CANONMODE)
tty_new.c_lflag |= CANONMODE;
#undef CANONMODE
(void) ioctl(SHIN, TCSETAW, (char *)&tty_new);
}
(void) sigsetmask(omask);
/* END of tty initialization block that was setup_tty(ON) */
termchars();
nread = 0;
should_retype = FALSE;
while((i = read_(SHIN, iline + nread, ilsize - nread)) >= 0) {
register wchar_t *str_end, *word_start, lastc;
register int space_left;
struct termio tty;
COMMAND command;
nread += i;
iline[nread] = '\0'; /* terminate string */
lastc = iline[nread - 1] & TRIM; /* last char */
/* Check if nread is 0. If so, then lastc is meaningless as we
may be comparing against garbage which may cause unpredictable
behaviour. Moved the check for nread to the top. This should be
done first.
*/
if( !nread) {
errno = 0; /* but no error */
break;
}
if((nread == ilsize) || (lastc == '\n'))
break;
str_end = iline + nread;
if(lastc == ESC) {
command = RECOGNIZE;
*--str_end = '\0'; /* wipe out trailing ESC */
} else
command = LIST;
tty = tty_new; /* set tty */
tty.c_lflag &= ~ECHO;
(void) ioctl(SHIN, TCSETAF, (char *)&tty);
if(command == LIST)
shprintf("\n");
/*
* Find LAST occurence of a delimiter in the iline.
* The word start is one character past it.
*/
for(word_start = str_end; word_start > iline; --word_start) {
if(wschr(S_DELIM, word_start[-1]) || isauxsp(word_start[-1]))
break;
}
space_left = ilsize - (word_start - iline) - 1;
nitems = search2(word_start, command, space_left);
if(command == RECOGNIZE) {
precstuff(str_end); /* print from str_end on */
if(nitems != 1) /* Beep = No match/ambiguous */
beep();
}
/*
* 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(wschr(iline, '\t')) { /* tab in input line? */
back_to_col_1();
should_retype = TRUE;
}
if(command == LIST) /* Always retype after a LIST */
should_retype = TRUE;
if(should_retype)
printprompt();
pushback(iline, should_retype);
nread = 0; /* chars will be reread */
should_retype = FALSE;
}
/* START of tty initialization block that was setup_tty(OFF) */
/* Reset terminal state to what user had when invoked */
omask = sigblock(sigmask(SIGINT));
update_tty(&tty_save);
(void) ioctl(SHIN, TCSETAW, (char *)&tty_save);
resexit(osetexit);
(void) sigsetmask(omask);
/* END of tty initialization block that was setup_tty(OFF) */
return(nread? nread : -1);
}
static bool
ignored(wchar_t *entry)
{
struct varent *vp;
register wchar_t **cp;
#ifdef TRACE
tprintf("TRACE- ignored()\n");
#endif
if( !(vp = adrof(S_fignore)) || !(cp = vp->vec))
return(FALSE);
for(; *cp; cp++) {
if(is_suffix(entry, *cp))
return(TRUE);
}
return(FALSE);
}