1*9448619aSanton /* $OpenBSD: file.c,v 1.26 2017/06/21 19:36:23 anton Exp $ */ 226b220adSmillert /* $NetBSD: file.c,v 1.11 1996/11/08 19:34:37 christos Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /*- 5df930be7Sderaadt * Copyright (c) 1980, 1991, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 1629295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 17df930be7Sderaadt * may be used to endorse or promote products derived from this software 18df930be7Sderaadt * without specific prior written permission. 19df930be7Sderaadt * 20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30df930be7Sderaadt * SUCH DAMAGE. 31df930be7Sderaadt */ 32df930be7Sderaadt 33df930be7Sderaadt #include <sys/ioctl.h> 34df930be7Sderaadt #include <sys/stat.h> 35*9448619aSanton #include <sys/types.h> 36*9448619aSanton 37df930be7Sderaadt #include <dirent.h> 38c0d918cdSanton #include <errno.h> 39*9448619aSanton #include <limits.h> 40df930be7Sderaadt #include <pwd.h> 41df930be7Sderaadt #include <stdlib.h> 42c0d918cdSanton #include <string.h> 43*9448619aSanton #include <termios.h> 44df930be7Sderaadt #include <unistd.h> 45df930be7Sderaadt 46df930be7Sderaadt #include "csh.h" 47df930be7Sderaadt #include "extern.h" 48df930be7Sderaadt 49df930be7Sderaadt /* 50df930be7Sderaadt * Tenex style file name recognition, .. and more. 51df930be7Sderaadt * History: 52df930be7Sderaadt * Author: Ken Greer, Sept. 1975, CMU. 53df930be7Sderaadt * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 54df930be7Sderaadt */ 55df930be7Sderaadt 56df930be7Sderaadt #ifndef TRUE 57df930be7Sderaadt #define TRUE 1 58df930be7Sderaadt #endif 59df930be7Sderaadt #ifndef FALSE 60df930be7Sderaadt #define FALSE 0 61df930be7Sderaadt #endif 62df930be7Sderaadt 63df930be7Sderaadt #define ESC '\033' 64c0d918cdSanton #define TABWIDTH 8 65c0d918cdSanton 66df930be7Sderaadt typedef enum { 67*9448619aSanton LIST, 68*9448619aSanton RECOGNIZE 69df930be7Sderaadt } COMMAND; 70df930be7Sderaadt 71c0d918cdSanton struct cmdline { 72c0d918cdSanton int fdin; 73c0d918cdSanton int fdout; 74c0d918cdSanton int flags; 75c0d918cdSanton #define CL_ALTWERASE 0x1 76c0d918cdSanton #define CL_PROMPT 0x2 77c0d918cdSanton char *buf; 78c0d918cdSanton size_t len; 79c0d918cdSanton size_t size; 80c0d918cdSanton size_t cursor; 81c0d918cdSanton }; 82c0d918cdSanton 83c0d918cdSanton /* Command line auxiliary functions. */ 84c0d918cdSanton static void cl_beep(struct cmdline *); 85c0d918cdSanton static void cl_flush(struct cmdline *); 86c0d918cdSanton static int cl_getc(struct cmdline *); 87c0d918cdSanton static Char *cl_lastw(struct cmdline *); 88c0d918cdSanton static void cl_putc(struct cmdline *, int); 89c0d918cdSanton static void cl_visc(struct cmdline *, int); 90c0d918cdSanton 91c0d918cdSanton /* Command line editing functions. */ 92c0d918cdSanton static int cl_abort(struct cmdline *, int); 93c0d918cdSanton static int cl_erasec(struct cmdline *, int); 94c0d918cdSanton static int cl_erasew(struct cmdline *, int); 95c0d918cdSanton static int cl_insert(struct cmdline *, int); 96c0d918cdSanton static int cl_kill(struct cmdline *, int); 97c0d918cdSanton static int cl_list(struct cmdline *, int); 98c0d918cdSanton static int cl_literal(struct cmdline *, int); 99c0d918cdSanton static int cl_recognize(struct cmdline *, int); 100c0d918cdSanton static int cl_reprint(struct cmdline *, int); 101c0d918cdSanton static int cl_status(struct cmdline *, int); 102c0d918cdSanton 103c0d918cdSanton static const struct termios *setup_tty(int); 104c0d918cdSanton 105c72b5b24Smillert static void catn(Char *, Char *, int); 106c72b5b24Smillert static void copyn(Char *, Char *, int); 107c72b5b24Smillert static Char filetype(Char *, Char *); 108c72b5b24Smillert static void print_by_column(Char *, Char *[], int); 109c72b5b24Smillert static Char *tilde(Char *, Char *); 110c72b5b24Smillert static void extract_dir_and_name(Char *, Char *, Char *); 111c72b5b24Smillert static Char *getentry(DIR *, int); 112b1f6b197Smillert static void free_items(Char **, int); 113c72b5b24Smillert static int tsearch(Char *, COMMAND, int); 114c72b5b24Smillert static int recognize(Char *, Char *, int, int); 115c72b5b24Smillert static int is_prefix(Char *, Char *); 116c72b5b24Smillert static int is_suffix(Char *, Char *); 117c72b5b24Smillert static int ignored(Char *); 118df930be7Sderaadt 119df930be7Sderaadt /* 120df930be7Sderaadt * Put this here so the binary can be patched with adb to enable file 121df930be7Sderaadt * completion by default. Filec controls completion, nobeep controls 122df930be7Sderaadt * ringing the terminal bell on incomplete expansions. 123df930be7Sderaadt */ 124df930be7Sderaadt bool filec = 0; 125df930be7Sderaadt 126df930be7Sderaadt static void 127c0d918cdSanton cl_flush(struct cmdline *cl) 128c0d918cdSanton { 129c0d918cdSanton size_t i, len; 130c0d918cdSanton int c; 131c0d918cdSanton 132c0d918cdSanton if (cl->flags & CL_PROMPT) { 133c0d918cdSanton cl->flags &= ~CL_PROMPT; 134c0d918cdSanton printprompt(); 135c0d918cdSanton } 136c0d918cdSanton 137c0d918cdSanton if (cl->cursor < cl->len) { 138c0d918cdSanton for (; cl->cursor < cl->len; cl->cursor++) 139c0d918cdSanton cl_visc(cl, cl->buf[cl->cursor]); 140c0d918cdSanton } else if (cl->cursor > cl->len) { 141c0d918cdSanton len = cl->cursor - cl->len; 142c0d918cdSanton for (i = len; i > 0; i--) { 143c0d918cdSanton c = cl->buf[--cl->cursor]; 144c0d918cdSanton if (c == '\t') 145c0d918cdSanton len += TABWIDTH - 1; 146c0d918cdSanton else if (iscntrl(c)) 147c0d918cdSanton len++; /* account for leading ^ */ 148c0d918cdSanton } 149c0d918cdSanton for (i = 0; i < len; i++) 150c0d918cdSanton cl_putc(cl, '\b'); 151c0d918cdSanton for (i = 0; i < len; i++) 152c0d918cdSanton cl_putc(cl, ' '); 153c0d918cdSanton for (i = 0; i < len; i++) 154c0d918cdSanton cl_putc(cl, '\b'); 155c0d918cdSanton cl->cursor = cl->len; 156c0d918cdSanton } 157c0d918cdSanton } 158c0d918cdSanton 159c0d918cdSanton static int 160c0d918cdSanton cl_getc(struct cmdline *cl) 161c0d918cdSanton { 162c0d918cdSanton ssize_t n; 163c0d918cdSanton int c; 164c0d918cdSanton 165c0d918cdSanton for (;;) { 166c0d918cdSanton n = read(cl->fdin, &c, 1); 167c0d918cdSanton switch (n) { 168c0d918cdSanton case -1: 169c0d918cdSanton if (errno == EINTR) 170c0d918cdSanton continue; 171c0d918cdSanton /* FALLTHROUGH */ 172c0d918cdSanton case 0: 173c0d918cdSanton return 0; 174c0d918cdSanton default: 175c0d918cdSanton return c & 0x7F; 176c0d918cdSanton } 177c0d918cdSanton } 178c0d918cdSanton } 179c0d918cdSanton 180c0d918cdSanton static Char * 181c0d918cdSanton cl_lastw(struct cmdline *cl) 182c0d918cdSanton { 183c0d918cdSanton static Char word[BUFSIZ]; 184c0d918cdSanton const char *delimiters = " '\"\t;&<>()|^%"; 185c0d918cdSanton Char *cp; 186c0d918cdSanton size_t i; 187c0d918cdSanton 188c0d918cdSanton for (i = cl->len; i > 0; i--) 189c0d918cdSanton if (strchr(delimiters, cl->buf[i - 1]) != NULL) 190c0d918cdSanton break; 191c0d918cdSanton 192c0d918cdSanton cp = word; 193c0d918cdSanton for (; i < cl->len; i++) 194c0d918cdSanton *cp++ = cl->buf[i]; 195c0d918cdSanton *cp = '\0'; 196c0d918cdSanton 197c0d918cdSanton return word; 198c0d918cdSanton } 199c0d918cdSanton 200c0d918cdSanton static void 201c0d918cdSanton cl_putc(struct cmdline *cl, int c) 202c0d918cdSanton { 203c0d918cdSanton write(cl->fdout, &c, 1); 204c0d918cdSanton } 205c0d918cdSanton 206c0d918cdSanton static void 207c0d918cdSanton cl_visc(struct cmdline *cl, int c) 208c0d918cdSanton { 209c0d918cdSanton #define UNCNTRL(x) ((x) == 0x7F ? '?' : ((x) | 0x40)) 210c0d918cdSanton int i; 211c0d918cdSanton 212c0d918cdSanton if (c == '\t') { 213c0d918cdSanton for (i = 0; i < TABWIDTH; i++) 214c0d918cdSanton cl_putc(cl, ' '); 215c0d918cdSanton } else if (c != '\n' && iscntrl(c)) { 216c0d918cdSanton cl_putc(cl, '^'); 217c0d918cdSanton cl_putc(cl, UNCNTRL(c)); 218c0d918cdSanton } else { 219c0d918cdSanton cl_putc(cl, c); 220c0d918cdSanton } 221c0d918cdSanton } 222c0d918cdSanton 223c0d918cdSanton static int 224c0d918cdSanton cl_abort(struct cmdline *cl, int c) 225c0d918cdSanton { 226c0d918cdSanton cl_visc(cl, c); 227c0d918cdSanton cl_putc(cl, '\n'); 228c0d918cdSanton cl->len = cl->cursor = 0; 229c0d918cdSanton cl->flags |= CL_PROMPT; 230c0d918cdSanton 231c0d918cdSanton return 0; 232c0d918cdSanton } 233c0d918cdSanton 234c0d918cdSanton static int 235c0d918cdSanton cl_erasec(struct cmdline *cl, int c) 236c0d918cdSanton { 237c0d918cdSanton if (cl->len > 0) 238c0d918cdSanton cl->len--; 239c0d918cdSanton 240c0d918cdSanton return 0; 241c0d918cdSanton } 242c0d918cdSanton 243c0d918cdSanton static int 244c0d918cdSanton cl_erasew(struct cmdline *cl, int c) 245c0d918cdSanton { 246c0d918cdSanton const char *ws = " \t"; 247c0d918cdSanton 248c0d918cdSanton for (; cl->len > 0; cl->len--) 249c0d918cdSanton if (strchr(ws, cl->buf[cl->len - 1]) == NULL 250c0d918cdSanton && ((cl->flags & CL_ALTWERASE) == 0 251c0d918cdSanton || isalpha(cl->buf[cl->len - 1]))) 252c0d918cdSanton break; 253c0d918cdSanton for (; cl->len > 0; cl->len--) 254c0d918cdSanton if (strchr(ws, cl->buf[cl->len - 1]) != NULL 255c0d918cdSanton || ((cl->flags & CL_ALTWERASE) 256c0d918cdSanton && !isalpha(cl->buf[cl->len - 1]))) 257c0d918cdSanton break; 258c0d918cdSanton 259c0d918cdSanton return 0; 260c0d918cdSanton } 261c0d918cdSanton 262c0d918cdSanton static void 263c0d918cdSanton cl_beep(struct cmdline *cl) 264c0d918cdSanton { 265c0d918cdSanton if (adrof(STRnobeep) == 0) 266c0d918cdSanton cl_putc(cl, '\007'); 267c0d918cdSanton } 268c0d918cdSanton 269c0d918cdSanton static int 270c0d918cdSanton cl_insert(struct cmdline *cl, int c) 271c0d918cdSanton { 272c0d918cdSanton if (cl->len == cl->size) 273c0d918cdSanton return 1; 274c0d918cdSanton 275c0d918cdSanton cl->buf[cl->len++] = c; 276c0d918cdSanton 277c0d918cdSanton if (c == '\n') 278c0d918cdSanton return 1; 279c0d918cdSanton 280c0d918cdSanton return 0; 281c0d918cdSanton } 282c0d918cdSanton 283c0d918cdSanton static int 284c0d918cdSanton cl_kill(struct cmdline *cl, int c) 285c0d918cdSanton { 286c0d918cdSanton cl->len = 0; 287c0d918cdSanton 288c0d918cdSanton return 0; 289c0d918cdSanton } 290c0d918cdSanton 291c0d918cdSanton static int 292c0d918cdSanton cl_list(struct cmdline *cl, int c) 293c0d918cdSanton { 294c0d918cdSanton Char *word; 295c0d918cdSanton size_t len; 296c0d918cdSanton 297c0d918cdSanton if (adrof(STRignoreeof) || cl->len > 0) 298c0d918cdSanton cl_visc(cl, c); 299c0d918cdSanton 300c0d918cdSanton if (cl->len == 0) 301c0d918cdSanton return 1; 302c0d918cdSanton 303c0d918cdSanton cl_putc(cl, '\n'); 304c0d918cdSanton cl->cursor = 0; 305c0d918cdSanton cl->flags |= CL_PROMPT; 306c0d918cdSanton 307c0d918cdSanton word = cl_lastw(cl); 308c0d918cdSanton len = Strlen(word); 309c0d918cdSanton tsearch(word, LIST, BUFSIZ - len - 1); /* NUL */ 310c0d918cdSanton 311c0d918cdSanton return 0; 312c0d918cdSanton } 313c0d918cdSanton 314c0d918cdSanton static int 315c0d918cdSanton cl_literal(struct cmdline *cl, int c) 316c0d918cdSanton { 317c0d918cdSanton int literal; 318c0d918cdSanton 319c0d918cdSanton literal = cl_getc(cl); 320c0d918cdSanton if (literal == '\n') 321c0d918cdSanton literal = '\r'; 322c0d918cdSanton cl_insert(cl, literal); 323c0d918cdSanton 324c0d918cdSanton return 0; 325c0d918cdSanton } 326c0d918cdSanton 327c0d918cdSanton static int 328c0d918cdSanton cl_recognize(struct cmdline *cl, int c) 329c0d918cdSanton { 330c0d918cdSanton Char *word; 331c0d918cdSanton size_t len; 332c0d918cdSanton int nitems; 333c0d918cdSanton 334c0d918cdSanton if (cl->len == 0) { 335c0d918cdSanton cl_beep(cl); 336c0d918cdSanton return 0; 337c0d918cdSanton } 338c0d918cdSanton 339c0d918cdSanton word = cl_lastw(cl); 340c0d918cdSanton len = Strlen(word); 341c0d918cdSanton nitems = tsearch(word, RECOGNIZE, BUFSIZ - len - 1); /* NUL */ 342c0d918cdSanton for (word += len; *word != '\0'; word++) 343c0d918cdSanton cl_insert(cl, *word); 344c0d918cdSanton if (nitems != 1) 345c0d918cdSanton cl_beep(cl); 346c0d918cdSanton 347c0d918cdSanton return 0; 348c0d918cdSanton } 349c0d918cdSanton 350c0d918cdSanton static int 351c0d918cdSanton cl_reprint(struct cmdline *cl, int c) 352c0d918cdSanton { 353c0d918cdSanton cl_visc(cl, c); 354c0d918cdSanton cl_putc(cl, '\n'); 355c0d918cdSanton cl->cursor = 0; 356c0d918cdSanton 357c0d918cdSanton return 0; 358c0d918cdSanton } 359c0d918cdSanton 360c0d918cdSanton static int 361c0d918cdSanton cl_status(struct cmdline *cl, int c) 362c0d918cdSanton { 363c0d918cdSanton int nothing = 0; 364c0d918cdSanton 365c0d918cdSanton cl->cursor = 0; 366c0d918cdSanton ioctl(cl->fdin, TIOCSTAT, ¬hing); 367c0d918cdSanton 368c0d918cdSanton return 0; 369c0d918cdSanton } 370c0d918cdSanton 371c0d918cdSanton const struct termios * 372e757c91eSderaadt setup_tty(int on) 373df930be7Sderaadt { 374c0d918cdSanton static struct termios newtio, oldtio; 375df930be7Sderaadt 376df930be7Sderaadt if (on) { 377c0d918cdSanton tcgetattr(SHIN, &oldtio); 378c0d918cdSanton 379c0d918cdSanton newtio = oldtio; 380c0d918cdSanton newtio.c_lflag &= ~(ECHO | ICANON | ISIG); 381c0d918cdSanton newtio.c_cc[VEOL] = ESC; 382c0d918cdSanton newtio.c_cc[VLNEXT] = _POSIX_VDISABLE; 383c0d918cdSanton newtio.c_cc[VMIN] = 1; 384c0d918cdSanton newtio.c_cc[VTIME] = 0; 385c0d918cdSanton } else { 386c0d918cdSanton newtio = oldtio; 387df930be7Sderaadt } 388df930be7Sderaadt 389c0d918cdSanton tcsetattr(SHIN, TCSADRAIN, &newtio); 390df930be7Sderaadt 391df930be7Sderaadt /* 392c0d918cdSanton * Since VLNEXT is disabled, restore its previous value in order to make 393c0d918cdSanton * the key detectable. 394df930be7Sderaadt */ 395c0d918cdSanton newtio.c_cc[VLNEXT] = oldtio.c_cc[VLNEXT]; 396df930be7Sderaadt 397c0d918cdSanton return &newtio; 398df930be7Sderaadt } 399df930be7Sderaadt 400df930be7Sderaadt /* 401df930be7Sderaadt * Concatenate src onto tail of des. 402df930be7Sderaadt * Des is a string whose maximum length is count. 403df930be7Sderaadt * Always null terminate. 404df930be7Sderaadt */ 405df930be7Sderaadt static void 406e757c91eSderaadt catn(Char *des, Char *src, int count) 407df930be7Sderaadt { 408df930be7Sderaadt while (--count >= 0 && *des) 409df930be7Sderaadt des++; 410df930be7Sderaadt while (--count >= 0) 411df930be7Sderaadt if ((*des++ = *src++) == 0) 412df930be7Sderaadt return; 413df930be7Sderaadt *des = '\0'; 414df930be7Sderaadt } 415df930be7Sderaadt 416df930be7Sderaadt /* 417aead66f0Sderaadt * Places Char's like strlcpy, but no special return value. 418df930be7Sderaadt */ 419df930be7Sderaadt static void 420e757c91eSderaadt copyn(Char *des, Char *src, int count) 421df930be7Sderaadt { 422df930be7Sderaadt while (--count >= 0) 423df930be7Sderaadt if ((*des++ = *src++) == 0) 424df930be7Sderaadt return; 425df930be7Sderaadt *des = '\0'; 426df930be7Sderaadt } 427df930be7Sderaadt 428df930be7Sderaadt static Char 429e757c91eSderaadt filetype(Char *dir, Char *file) 430df930be7Sderaadt { 431b9fc9a72Sderaadt Char path[PATH_MAX]; 432df930be7Sderaadt struct stat statb; 433df930be7Sderaadt 4346a01f4acSderaadt Strlcpy(path, dir, sizeof path/sizeof(Char)); 4356a01f4acSderaadt catn(path, file, sizeof(path) / sizeof(Char)); 436df930be7Sderaadt if (lstat(short2str(path), &statb) == 0) { 437df930be7Sderaadt switch (statb.st_mode & S_IFMT) { 438df930be7Sderaadt case S_IFDIR: 439df930be7Sderaadt return ('/'); 440df930be7Sderaadt 441df930be7Sderaadt case S_IFLNK: 442df930be7Sderaadt if (stat(short2str(path), &statb) == 0 && /* follow it out */ 443df930be7Sderaadt S_ISDIR(statb.st_mode)) 444df930be7Sderaadt return ('>'); 445df930be7Sderaadt else 446df930be7Sderaadt return ('@'); 447df930be7Sderaadt 448df930be7Sderaadt case S_IFSOCK: 449df930be7Sderaadt return ('='); 450df930be7Sderaadt 451df930be7Sderaadt default: 452df930be7Sderaadt if (statb.st_mode & 0111) 453df930be7Sderaadt return ('*'); 454df930be7Sderaadt } 455df930be7Sderaadt } 456df930be7Sderaadt return (' '); 457df930be7Sderaadt } 458df930be7Sderaadt 459df930be7Sderaadt /* 460df930be7Sderaadt * Print sorted down columns 461df930be7Sderaadt */ 462df930be7Sderaadt static void 463e757c91eSderaadt print_by_column(Char *dir, Char *items[], int count) 464df930be7Sderaadt { 4650134f510Santon struct winsize win; 466e757c91eSderaadt int i, rows, r, c, maxwidth = 0, columns; 467df930be7Sderaadt 468df930be7Sderaadt if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 469df930be7Sderaadt win.ws_col = 80; 470df930be7Sderaadt for (i = 0; i < count; i++) 471df930be7Sderaadt maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 472df930be7Sderaadt maxwidth += 2; /* for the file tag and space */ 473df930be7Sderaadt columns = win.ws_col / maxwidth; 474df930be7Sderaadt if (columns == 0) 475df930be7Sderaadt columns = 1; 476df930be7Sderaadt rows = (count + (columns - 1)) / columns; 477df930be7Sderaadt for (r = 0; r < rows; r++) { 478df930be7Sderaadt for (c = 0; c < columns; c++) { 479df930be7Sderaadt i = c * rows + r; 480df930be7Sderaadt if (i < count) { 481e757c91eSderaadt int w; 482df930be7Sderaadt 483df930be7Sderaadt (void) fprintf(cshout, "%s", vis_str(items[i])); 484df930be7Sderaadt (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); 485df930be7Sderaadt if (c < columns - 1) { /* last column? */ 486df930be7Sderaadt w = Strlen(items[i]) + 1; 487df930be7Sderaadt for (; w < maxwidth; w++) 488df930be7Sderaadt (void) fputc(' ', cshout); 489df930be7Sderaadt } 490df930be7Sderaadt } 491df930be7Sderaadt } 492df930be7Sderaadt (void) fputc('\r', cshout); 493df930be7Sderaadt (void) fputc('\n', cshout); 494df930be7Sderaadt } 495df930be7Sderaadt } 496df930be7Sderaadt 497df930be7Sderaadt /* 498df930be7Sderaadt * Expand file name with possible tilde usage 499df930be7Sderaadt * ~person/mumble 500df930be7Sderaadt * expands to 501df930be7Sderaadt * home_directory_of_person/mumble 502df930be7Sderaadt */ 503df930be7Sderaadt static Char * 504e757c91eSderaadt tilde(Char *new, Char *old) 505df930be7Sderaadt { 506e757c91eSderaadt Char *o, *p; 507e757c91eSderaadt struct passwd *pw; 508df930be7Sderaadt static Char person[40]; 509df930be7Sderaadt 5106a01f4acSderaadt if (old[0] != '~') { 511b9fc9a72Sderaadt Strlcpy(new, old, PATH_MAX); 5126a01f4acSderaadt return new; 5136a01f4acSderaadt } 514df930be7Sderaadt 515df930be7Sderaadt for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) 516df930be7Sderaadt continue; 517df930be7Sderaadt *p = '\0'; 518df930be7Sderaadt if (person[0] == '\0') 519b9fc9a72Sderaadt (void) Strlcpy(new, value(STRhome), PATH_MAX); 520df930be7Sderaadt else { 521df930be7Sderaadt pw = getpwnam(short2str(person)); 522df930be7Sderaadt if (pw == NULL) 523df930be7Sderaadt return (NULL); 524b9fc9a72Sderaadt (void) Strlcpy(new, str2short(pw->pw_dir), PATH_MAX); 525df930be7Sderaadt } 526b9fc9a72Sderaadt (void) Strlcat(new, o, PATH_MAX); 527df930be7Sderaadt return (new); 528df930be7Sderaadt } 529df930be7Sderaadt 530df930be7Sderaadt /* 531df930be7Sderaadt * Parse full path in file into 2 parts: directory and file names 532df930be7Sderaadt * Should leave final slash (/) at end of dir. 533df930be7Sderaadt */ 534df930be7Sderaadt static void 535e757c91eSderaadt extract_dir_and_name(Char *path, Char *dir, Char *name) 536df930be7Sderaadt { 537e757c91eSderaadt Char *p; 538df930be7Sderaadt 539df930be7Sderaadt p = Strrchr(path, '/'); 540df930be7Sderaadt if (p == NULL) { 541df930be7Sderaadt copyn(name, path, MAXNAMLEN); 542df930be7Sderaadt dir[0] = '\0'; 543df930be7Sderaadt } 544df930be7Sderaadt else { 545df930be7Sderaadt copyn(name, ++p, MAXNAMLEN); 546df930be7Sderaadt copyn(dir, path, p - path); 547df930be7Sderaadt } 548df930be7Sderaadt } 549df930be7Sderaadt 550df930be7Sderaadt static Char * 551e757c91eSderaadt getentry(DIR *dir_fd, int looking_for_lognames) 552df930be7Sderaadt { 553e757c91eSderaadt struct passwd *pw; 554e757c91eSderaadt struct dirent *dirp; 555df930be7Sderaadt 556df930be7Sderaadt if (looking_for_lognames) { 557df930be7Sderaadt if ((pw = getpwent()) == NULL) 558df930be7Sderaadt return (NULL); 559df930be7Sderaadt return (str2short(pw->pw_name)); 560df930be7Sderaadt } 561df930be7Sderaadt if ((dirp = readdir(dir_fd)) != NULL) 562df930be7Sderaadt return (str2short(dirp->d_name)); 563df930be7Sderaadt return (NULL); 564df930be7Sderaadt } 565df930be7Sderaadt 566df930be7Sderaadt static void 567e757c91eSderaadt free_items(Char **items, int numitems) 568df930be7Sderaadt { 569b1f6b197Smillert int i; 570df930be7Sderaadt 571b1f6b197Smillert for (i = 0; i < numitems; i++) 572acdb3202Smestre free(items[i]); 573acdb3202Smestre free(items); 574df930be7Sderaadt } 575df930be7Sderaadt 576df930be7Sderaadt #define FREE_ITEMS(items) { \ 577df930be7Sderaadt sigset_t sigset, osigset;\ 578df930be7Sderaadt \ 579df930be7Sderaadt sigemptyset(&sigset);\ 580df930be7Sderaadt sigaddset(&sigset, SIGINT);\ 581df930be7Sderaadt sigprocmask(SIG_BLOCK, &sigset, &osigset);\ 582b1f6b197Smillert free_items(items, numitems);\ 583df930be7Sderaadt sigprocmask(SIG_SETMASK, &osigset, NULL);\ 584df930be7Sderaadt } 585df930be7Sderaadt 586df930be7Sderaadt /* 587df930be7Sderaadt * Perform a RECOGNIZE or LIST command on string "word". 588df930be7Sderaadt */ 589df930be7Sderaadt static int 590e757c91eSderaadt tsearch(Char *word, COMMAND command, int max_word_length) 591df930be7Sderaadt { 592e757c91eSderaadt DIR *dir_fd; 593e757c91eSderaadt int numitems = 0, ignoring = TRUE, nignored = 0; 594e757c91eSderaadt int name_length, looking_for_lognames; 595b9fc9a72Sderaadt Char tilded_dir[PATH_MAX], dir[PATH_MAX]; 596df930be7Sderaadt Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 597df930be7Sderaadt Char *entry; 5989a5c8861Smillert Char **items = NULL; 5999a5c8861Smillert size_t maxitems = 0; 600df930be7Sderaadt 601df930be7Sderaadt looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 602df930be7Sderaadt if (looking_for_lognames) { 603df930be7Sderaadt (void) setpwent(); 604df930be7Sderaadt copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 605df930be7Sderaadt dir_fd = NULL; 606df930be7Sderaadt } 607df930be7Sderaadt else { 608df930be7Sderaadt extract_dir_and_name(word, dir, name); 609df930be7Sderaadt if (tilde(tilded_dir, dir) == 0) 610df930be7Sderaadt return (0); 611df930be7Sderaadt dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 612df930be7Sderaadt if (dir_fd == NULL) 613df930be7Sderaadt return (0); 614df930be7Sderaadt } 615df930be7Sderaadt 616df930be7Sderaadt again: /* search for matches */ 617df930be7Sderaadt name_length = Strlen(name); 618df930be7Sderaadt for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { 619df930be7Sderaadt if (!is_prefix(name, entry)) 620df930be7Sderaadt continue; 621df930be7Sderaadt /* Don't match . files on null prefix match */ 622df930be7Sderaadt if (name_length == 0 && entry[0] == '.' && 623df930be7Sderaadt !looking_for_lognames) 624df930be7Sderaadt continue; 625df930be7Sderaadt if (command == LIST) { 6269a5c8861Smillert if (numitems >= maxitems) { 6279a5c8861Smillert maxitems += 1024; 628a8627d2cSderaadt items = xreallocarray(items, maxitems, sizeof(*items)); 6299a5c8861Smillert } 630a8627d2cSderaadt items[numitems] = xreallocarray(NULL, (Strlen(entry) + 1), sizeof(Char)); 631df930be7Sderaadt copyn(items[numitems], entry, MAXNAMLEN); 632df930be7Sderaadt numitems++; 633df930be7Sderaadt } 634df930be7Sderaadt else { /* RECOGNIZE command */ 635df930be7Sderaadt if (ignoring && ignored(entry)) 636df930be7Sderaadt nignored++; 637df930be7Sderaadt else if (recognize(extended_name, 638df930be7Sderaadt entry, name_length, ++numitems)) 639df930be7Sderaadt break; 640df930be7Sderaadt } 641df930be7Sderaadt } 642df930be7Sderaadt if (ignoring && numitems == 0 && nignored > 0) { 643df930be7Sderaadt ignoring = FALSE; 644df930be7Sderaadt nignored = 0; 645df930be7Sderaadt if (looking_for_lognames) 646df930be7Sderaadt (void) setpwent(); 647df930be7Sderaadt else 648df930be7Sderaadt rewinddir(dir_fd); 649df930be7Sderaadt goto again; 650df930be7Sderaadt } 651df930be7Sderaadt 652df930be7Sderaadt if (looking_for_lognames) 653df930be7Sderaadt (void) endpwent(); 654df930be7Sderaadt else 655df930be7Sderaadt (void) closedir(dir_fd); 656df930be7Sderaadt if (numitems == 0) 657df930be7Sderaadt return (0); 658df930be7Sderaadt if (command == RECOGNIZE) { 659df930be7Sderaadt if (looking_for_lognames) 660df930be7Sderaadt copyn(word, STRtilde, 1); 661df930be7Sderaadt else 662df930be7Sderaadt /* put back dir part */ 663df930be7Sderaadt copyn(word, dir, max_word_length); 664df930be7Sderaadt /* add extended name */ 665df930be7Sderaadt catn(word, extended_name, max_word_length); 666df930be7Sderaadt return (numitems); 667df930be7Sderaadt } 668df930be7Sderaadt else { /* LIST */ 669f4d574c6Stedu qsort(items, numitems, sizeof(*items), 670c72b5b24Smillert (int (*)(const void *, const void *)) sortscmp); 671df930be7Sderaadt print_by_column(looking_for_lognames ? NULL : tilded_dir, 672df930be7Sderaadt items, numitems); 673df930be7Sderaadt if (items != NULL) 674df930be7Sderaadt FREE_ITEMS(items); 675df930be7Sderaadt } 676df930be7Sderaadt return (0); 677df930be7Sderaadt } 678df930be7Sderaadt 679df930be7Sderaadt /* 680df930be7Sderaadt * Object: extend what user typed up to an ambiguity. 681df930be7Sderaadt * Algorithm: 682df930be7Sderaadt * On first match, copy full entry (assume it'll be the only match) 683df930be7Sderaadt * On subsequent matches, shorten extended_name to the first 684df930be7Sderaadt * Character mismatch between extended_name and entry. 685df930be7Sderaadt * If we shorten it back to the prefix length, stop searching. 686df930be7Sderaadt */ 687df930be7Sderaadt static int 688e757c91eSderaadt recognize(Char *extended_name, Char *entry, int name_length, int numitems) 689df930be7Sderaadt { 690df930be7Sderaadt if (numitems == 1) /* 1st match */ 691df930be7Sderaadt copyn(extended_name, entry, MAXNAMLEN); 692df930be7Sderaadt else { /* 2nd & subsequent matches */ 693e757c91eSderaadt Char *x, *ent; 694e757c91eSderaadt int len = 0; 695df930be7Sderaadt 696df930be7Sderaadt x = extended_name; 697df930be7Sderaadt for (ent = entry; *x && *x == *ent++; x++, len++) 698df930be7Sderaadt continue; 699df930be7Sderaadt *x = '\0'; /* Shorten at 1st Char diff */ 700df930be7Sderaadt if (len == name_length) /* Ambiguous to prefix? */ 701df930be7Sderaadt return (-1); /* So stop now and save time */ 702df930be7Sderaadt } 703df930be7Sderaadt return (0); 704df930be7Sderaadt } 705df930be7Sderaadt 706df930be7Sderaadt /* 707df930be7Sderaadt * Return true if check matches initial Chars in template. 708df930be7Sderaadt * This differs from PWB imatch in that if check is null 709df930be7Sderaadt * it matches anything. 710df930be7Sderaadt */ 711df930be7Sderaadt static int 712e757c91eSderaadt is_prefix(Char *check, Char *template) 713df930be7Sderaadt { 714df930be7Sderaadt do 715df930be7Sderaadt if (*check == 0) 716df930be7Sderaadt return (TRUE); 717df930be7Sderaadt while (*check++ == *template++); 718df930be7Sderaadt return (FALSE); 719df930be7Sderaadt } 720df930be7Sderaadt 721df930be7Sderaadt /* 722df930be7Sderaadt * Return true if the Chars in template appear at the 723df930be7Sderaadt * end of check, I.e., are it's suffix. 724df930be7Sderaadt */ 725df930be7Sderaadt static int 726e757c91eSderaadt is_suffix(Char *check, Char *template) 727df930be7Sderaadt { 728e757c91eSderaadt Char *c, *t; 729df930be7Sderaadt 730df930be7Sderaadt for (c = check; *c++;) 731df930be7Sderaadt continue; 732df930be7Sderaadt for (t = template; *t++;) 733df930be7Sderaadt continue; 734df930be7Sderaadt for (;;) { 735df930be7Sderaadt if (t == template) 736df930be7Sderaadt return 1; 737df930be7Sderaadt if (c == check || *--t != *--c) 738df930be7Sderaadt return 0; 739df930be7Sderaadt } 740df930be7Sderaadt } 741df930be7Sderaadt 742df930be7Sderaadt int 743e757c91eSderaadt tenex(Char *inputline, int inputline_size) 744df930be7Sderaadt { 745c0d918cdSanton static struct { 746c0d918cdSanton int (*fn)(struct cmdline *, int); 747c0d918cdSanton int idx; 748c0d918cdSanton } keys[] = { 749c0d918cdSanton { cl_abort, VINTR }, 750c0d918cdSanton { cl_erasec, VERASE }, 751c0d918cdSanton { cl_erasew, VWERASE }, 752c0d918cdSanton { cl_kill, VKILL }, 753c0d918cdSanton { cl_list, VEOF }, 754c0d918cdSanton { cl_literal, VLNEXT }, 755c0d918cdSanton { cl_recognize, VEOL }, 756c0d918cdSanton { cl_reprint, VREPRINT }, 757c0d918cdSanton { cl_status, VSTATUS }, 758c0d918cdSanton { cl_insert, -1 } 759c0d918cdSanton }; 760c0d918cdSanton char buf[BUFSIZ]; 761c0d918cdSanton const struct termios *tio; 762c0d918cdSanton struct cmdline cl; 763c0d918cdSanton size_t i; 764c0d918cdSanton int c, ret; 765df930be7Sderaadt 766c0d918cdSanton tio = setup_tty(1); 767df930be7Sderaadt 768c0d918cdSanton memset(&cl, 0, sizeof(cl)); 769c0d918cdSanton cl.fdin = SHIN; 770c0d918cdSanton cl.fdout = SHOUT; 771c0d918cdSanton cl.buf = buf; 772c0d918cdSanton cl.size = sizeof(buf); 773c0d918cdSanton if (tio->c_lflag & ALTWERASE) 774c0d918cdSanton cl.flags |= CL_ALTWERASE; 775df930be7Sderaadt 776c0d918cdSanton for (;;) { 777c0d918cdSanton if ((c = cl_getc(&cl)) == 0) 778df930be7Sderaadt break; 779c0d918cdSanton 780c0d918cdSanton for (i = 0; keys[i].idx >= 0; i++) 781c0d918cdSanton if (CCEQ(tio->c_cc[keys[i].idx], c)) 782df930be7Sderaadt break; 783c0d918cdSanton ret = keys[i].fn(&cl, c); 784c0d918cdSanton cl_flush(&cl); 785c0d918cdSanton if (ret) 786c0d918cdSanton break; 787df930be7Sderaadt } 788df930be7Sderaadt 789c0d918cdSanton setup_tty(0); 790c0d918cdSanton 791c0d918cdSanton for (i = 0; i < cl.len; i++) 792c0d918cdSanton inputline[i] = cl.buf[i]; 793c0d918cdSanton inputline[i] = '\0'; 794c0d918cdSanton 795c0d918cdSanton return cl.len; 796df930be7Sderaadt } 797df930be7Sderaadt 798df930be7Sderaadt static int 799e757c91eSderaadt ignored(Char *entry) 800df930be7Sderaadt { 801df930be7Sderaadt struct varent *vp; 802e757c91eSderaadt Char **cp; 803df930be7Sderaadt 804df930be7Sderaadt if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 805df930be7Sderaadt return (FALSE); 806df930be7Sderaadt for (; *cp != NULL; cp++) 807df930be7Sderaadt if (is_suffix(entry, *cp)) 808df930be7Sderaadt return (TRUE); 809df930be7Sderaadt return (FALSE); 810df930be7Sderaadt } 811