1*046eeb90Santon /* $OpenBSD: file.c,v 1.27 2017/06/22 18:05:31 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> 359448619aSanton #include <sys/types.h> 369448619aSanton 37df930be7Sderaadt #include <dirent.h> 38c0d918cdSanton #include <errno.h> 399448619aSanton #include <limits.h> 40df930be7Sderaadt #include <pwd.h> 41df930be7Sderaadt #include <stdlib.h> 42c0d918cdSanton #include <string.h> 439448619aSanton #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 { 679448619aSanton LIST, 689448619aSanton 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); 227*046eeb90Santon 228*046eeb90Santon /* Abort while/foreach loop prematurely. */ 229*046eeb90Santon if (whyles) 230*046eeb90Santon kill(getpid(), SIGINT); 231*046eeb90Santon 232c0d918cdSanton cl_putc(cl, '\n'); 233c0d918cdSanton cl->len = cl->cursor = 0; 234c0d918cdSanton cl->flags |= CL_PROMPT; 235c0d918cdSanton 236c0d918cdSanton return 0; 237c0d918cdSanton } 238c0d918cdSanton 239c0d918cdSanton static int 240c0d918cdSanton cl_erasec(struct cmdline *cl, int c) 241c0d918cdSanton { 242c0d918cdSanton if (cl->len > 0) 243c0d918cdSanton cl->len--; 244c0d918cdSanton 245c0d918cdSanton return 0; 246c0d918cdSanton } 247c0d918cdSanton 248c0d918cdSanton static int 249c0d918cdSanton cl_erasew(struct cmdline *cl, int c) 250c0d918cdSanton { 251c0d918cdSanton const char *ws = " \t"; 252c0d918cdSanton 253c0d918cdSanton for (; cl->len > 0; cl->len--) 254c0d918cdSanton if (strchr(ws, cl->buf[cl->len - 1]) == NULL 255c0d918cdSanton && ((cl->flags & CL_ALTWERASE) == 0 256c0d918cdSanton || isalpha(cl->buf[cl->len - 1]))) 257c0d918cdSanton break; 258c0d918cdSanton for (; cl->len > 0; cl->len--) 259c0d918cdSanton if (strchr(ws, cl->buf[cl->len - 1]) != NULL 260c0d918cdSanton || ((cl->flags & CL_ALTWERASE) 261c0d918cdSanton && !isalpha(cl->buf[cl->len - 1]))) 262c0d918cdSanton break; 263c0d918cdSanton 264c0d918cdSanton return 0; 265c0d918cdSanton } 266c0d918cdSanton 267c0d918cdSanton static void 268c0d918cdSanton cl_beep(struct cmdline *cl) 269c0d918cdSanton { 270c0d918cdSanton if (adrof(STRnobeep) == 0) 271c0d918cdSanton cl_putc(cl, '\007'); 272c0d918cdSanton } 273c0d918cdSanton 274c0d918cdSanton static int 275c0d918cdSanton cl_insert(struct cmdline *cl, int c) 276c0d918cdSanton { 277c0d918cdSanton if (cl->len == cl->size) 278c0d918cdSanton return 1; 279c0d918cdSanton 280c0d918cdSanton cl->buf[cl->len++] = c; 281c0d918cdSanton 282c0d918cdSanton if (c == '\n') 283c0d918cdSanton return 1; 284c0d918cdSanton 285c0d918cdSanton return 0; 286c0d918cdSanton } 287c0d918cdSanton 288c0d918cdSanton static int 289c0d918cdSanton cl_kill(struct cmdline *cl, int c) 290c0d918cdSanton { 291c0d918cdSanton cl->len = 0; 292c0d918cdSanton 293c0d918cdSanton return 0; 294c0d918cdSanton } 295c0d918cdSanton 296c0d918cdSanton static int 297c0d918cdSanton cl_list(struct cmdline *cl, int c) 298c0d918cdSanton { 299c0d918cdSanton Char *word; 300c0d918cdSanton size_t len; 301c0d918cdSanton 302c0d918cdSanton if (adrof(STRignoreeof) || cl->len > 0) 303c0d918cdSanton cl_visc(cl, c); 304c0d918cdSanton 305c0d918cdSanton if (cl->len == 0) 306c0d918cdSanton return 1; 307c0d918cdSanton 308c0d918cdSanton cl_putc(cl, '\n'); 309c0d918cdSanton cl->cursor = 0; 310c0d918cdSanton cl->flags |= CL_PROMPT; 311c0d918cdSanton 312c0d918cdSanton word = cl_lastw(cl); 313c0d918cdSanton len = Strlen(word); 314c0d918cdSanton tsearch(word, LIST, BUFSIZ - len - 1); /* NUL */ 315c0d918cdSanton 316c0d918cdSanton return 0; 317c0d918cdSanton } 318c0d918cdSanton 319c0d918cdSanton static int 320c0d918cdSanton cl_literal(struct cmdline *cl, int c) 321c0d918cdSanton { 322c0d918cdSanton int literal; 323c0d918cdSanton 324c0d918cdSanton literal = cl_getc(cl); 325c0d918cdSanton if (literal == '\n') 326c0d918cdSanton literal = '\r'; 327c0d918cdSanton cl_insert(cl, literal); 328c0d918cdSanton 329c0d918cdSanton return 0; 330c0d918cdSanton } 331c0d918cdSanton 332c0d918cdSanton static int 333c0d918cdSanton cl_recognize(struct cmdline *cl, int c) 334c0d918cdSanton { 335c0d918cdSanton Char *word; 336c0d918cdSanton size_t len; 337c0d918cdSanton int nitems; 338c0d918cdSanton 339c0d918cdSanton if (cl->len == 0) { 340c0d918cdSanton cl_beep(cl); 341c0d918cdSanton return 0; 342c0d918cdSanton } 343c0d918cdSanton 344c0d918cdSanton word = cl_lastw(cl); 345c0d918cdSanton len = Strlen(word); 346c0d918cdSanton nitems = tsearch(word, RECOGNIZE, BUFSIZ - len - 1); /* NUL */ 347c0d918cdSanton for (word += len; *word != '\0'; word++) 348c0d918cdSanton cl_insert(cl, *word); 349c0d918cdSanton if (nitems != 1) 350c0d918cdSanton cl_beep(cl); 351c0d918cdSanton 352c0d918cdSanton return 0; 353c0d918cdSanton } 354c0d918cdSanton 355c0d918cdSanton static int 356c0d918cdSanton cl_reprint(struct cmdline *cl, int c) 357c0d918cdSanton { 358c0d918cdSanton cl_visc(cl, c); 359c0d918cdSanton cl_putc(cl, '\n'); 360c0d918cdSanton cl->cursor = 0; 361c0d918cdSanton 362c0d918cdSanton return 0; 363c0d918cdSanton } 364c0d918cdSanton 365c0d918cdSanton static int 366c0d918cdSanton cl_status(struct cmdline *cl, int c) 367c0d918cdSanton { 368c0d918cdSanton int nothing = 0; 369c0d918cdSanton 370c0d918cdSanton cl->cursor = 0; 371c0d918cdSanton ioctl(cl->fdin, TIOCSTAT, ¬hing); 372c0d918cdSanton 373c0d918cdSanton return 0; 374c0d918cdSanton } 375c0d918cdSanton 376c0d918cdSanton const struct termios * 377e757c91eSderaadt setup_tty(int on) 378df930be7Sderaadt { 379c0d918cdSanton static struct termios newtio, oldtio; 380df930be7Sderaadt 381df930be7Sderaadt if (on) { 382c0d918cdSanton tcgetattr(SHIN, &oldtio); 383c0d918cdSanton 384c0d918cdSanton newtio = oldtio; 385c0d918cdSanton newtio.c_lflag &= ~(ECHO | ICANON | ISIG); 386c0d918cdSanton newtio.c_cc[VEOL] = ESC; 387c0d918cdSanton newtio.c_cc[VLNEXT] = _POSIX_VDISABLE; 388c0d918cdSanton newtio.c_cc[VMIN] = 1; 389c0d918cdSanton newtio.c_cc[VTIME] = 0; 390c0d918cdSanton } else { 391c0d918cdSanton newtio = oldtio; 392df930be7Sderaadt } 393df930be7Sderaadt 394c0d918cdSanton tcsetattr(SHIN, TCSADRAIN, &newtio); 395df930be7Sderaadt 396df930be7Sderaadt /* 397c0d918cdSanton * Since VLNEXT is disabled, restore its previous value in order to make 398c0d918cdSanton * the key detectable. 399df930be7Sderaadt */ 400c0d918cdSanton newtio.c_cc[VLNEXT] = oldtio.c_cc[VLNEXT]; 401df930be7Sderaadt 402c0d918cdSanton return &newtio; 403df930be7Sderaadt } 404df930be7Sderaadt 405df930be7Sderaadt /* 406df930be7Sderaadt * Concatenate src onto tail of des. 407df930be7Sderaadt * Des is a string whose maximum length is count. 408df930be7Sderaadt * Always null terminate. 409df930be7Sderaadt */ 410df930be7Sderaadt static void 411e757c91eSderaadt catn(Char *des, Char *src, int count) 412df930be7Sderaadt { 413df930be7Sderaadt while (--count >= 0 && *des) 414df930be7Sderaadt des++; 415df930be7Sderaadt while (--count >= 0) 416df930be7Sderaadt if ((*des++ = *src++) == 0) 417df930be7Sderaadt return; 418df930be7Sderaadt *des = '\0'; 419df930be7Sderaadt } 420df930be7Sderaadt 421df930be7Sderaadt /* 422aead66f0Sderaadt * Places Char's like strlcpy, but no special return value. 423df930be7Sderaadt */ 424df930be7Sderaadt static void 425e757c91eSderaadt copyn(Char *des, Char *src, int count) 426df930be7Sderaadt { 427df930be7Sderaadt while (--count >= 0) 428df930be7Sderaadt if ((*des++ = *src++) == 0) 429df930be7Sderaadt return; 430df930be7Sderaadt *des = '\0'; 431df930be7Sderaadt } 432df930be7Sderaadt 433df930be7Sderaadt static Char 434e757c91eSderaadt filetype(Char *dir, Char *file) 435df930be7Sderaadt { 436b9fc9a72Sderaadt Char path[PATH_MAX]; 437df930be7Sderaadt struct stat statb; 438df930be7Sderaadt 4396a01f4acSderaadt Strlcpy(path, dir, sizeof path/sizeof(Char)); 4406a01f4acSderaadt catn(path, file, sizeof(path) / sizeof(Char)); 441df930be7Sderaadt if (lstat(short2str(path), &statb) == 0) { 442df930be7Sderaadt switch (statb.st_mode & S_IFMT) { 443df930be7Sderaadt case S_IFDIR: 444df930be7Sderaadt return ('/'); 445df930be7Sderaadt 446df930be7Sderaadt case S_IFLNK: 447df930be7Sderaadt if (stat(short2str(path), &statb) == 0 && /* follow it out */ 448df930be7Sderaadt S_ISDIR(statb.st_mode)) 449df930be7Sderaadt return ('>'); 450df930be7Sderaadt else 451df930be7Sderaadt return ('@'); 452df930be7Sderaadt 453df930be7Sderaadt case S_IFSOCK: 454df930be7Sderaadt return ('='); 455df930be7Sderaadt 456df930be7Sderaadt default: 457df930be7Sderaadt if (statb.st_mode & 0111) 458df930be7Sderaadt return ('*'); 459df930be7Sderaadt } 460df930be7Sderaadt } 461df930be7Sderaadt return (' '); 462df930be7Sderaadt } 463df930be7Sderaadt 464df930be7Sderaadt /* 465df930be7Sderaadt * Print sorted down columns 466df930be7Sderaadt */ 467df930be7Sderaadt static void 468e757c91eSderaadt print_by_column(Char *dir, Char *items[], int count) 469df930be7Sderaadt { 4700134f510Santon struct winsize win; 471e757c91eSderaadt int i, rows, r, c, maxwidth = 0, columns; 472df930be7Sderaadt 473df930be7Sderaadt if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 474df930be7Sderaadt win.ws_col = 80; 475df930be7Sderaadt for (i = 0; i < count; i++) 476df930be7Sderaadt maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 477df930be7Sderaadt maxwidth += 2; /* for the file tag and space */ 478df930be7Sderaadt columns = win.ws_col / maxwidth; 479df930be7Sderaadt if (columns == 0) 480df930be7Sderaadt columns = 1; 481df930be7Sderaadt rows = (count + (columns - 1)) / columns; 482df930be7Sderaadt for (r = 0; r < rows; r++) { 483df930be7Sderaadt for (c = 0; c < columns; c++) { 484df930be7Sderaadt i = c * rows + r; 485df930be7Sderaadt if (i < count) { 486e757c91eSderaadt int w; 487df930be7Sderaadt 488df930be7Sderaadt (void) fprintf(cshout, "%s", vis_str(items[i])); 489df930be7Sderaadt (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); 490df930be7Sderaadt if (c < columns - 1) { /* last column? */ 491df930be7Sderaadt w = Strlen(items[i]) + 1; 492df930be7Sderaadt for (; w < maxwidth; w++) 493df930be7Sderaadt (void) fputc(' ', cshout); 494df930be7Sderaadt } 495df930be7Sderaadt } 496df930be7Sderaadt } 497df930be7Sderaadt (void) fputc('\r', cshout); 498df930be7Sderaadt (void) fputc('\n', cshout); 499df930be7Sderaadt } 500df930be7Sderaadt } 501df930be7Sderaadt 502df930be7Sderaadt /* 503df930be7Sderaadt * Expand file name with possible tilde usage 504df930be7Sderaadt * ~person/mumble 505df930be7Sderaadt * expands to 506df930be7Sderaadt * home_directory_of_person/mumble 507df930be7Sderaadt */ 508df930be7Sderaadt static Char * 509e757c91eSderaadt tilde(Char *new, Char *old) 510df930be7Sderaadt { 511e757c91eSderaadt Char *o, *p; 512e757c91eSderaadt struct passwd *pw; 513df930be7Sderaadt static Char person[40]; 514df930be7Sderaadt 5156a01f4acSderaadt if (old[0] != '~') { 516b9fc9a72Sderaadt Strlcpy(new, old, PATH_MAX); 5176a01f4acSderaadt return new; 5186a01f4acSderaadt } 519df930be7Sderaadt 520df930be7Sderaadt for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) 521df930be7Sderaadt continue; 522df930be7Sderaadt *p = '\0'; 523df930be7Sderaadt if (person[0] == '\0') 524b9fc9a72Sderaadt (void) Strlcpy(new, value(STRhome), PATH_MAX); 525df930be7Sderaadt else { 526df930be7Sderaadt pw = getpwnam(short2str(person)); 527df930be7Sderaadt if (pw == NULL) 528df930be7Sderaadt return (NULL); 529b9fc9a72Sderaadt (void) Strlcpy(new, str2short(pw->pw_dir), PATH_MAX); 530df930be7Sderaadt } 531b9fc9a72Sderaadt (void) Strlcat(new, o, PATH_MAX); 532df930be7Sderaadt return (new); 533df930be7Sderaadt } 534df930be7Sderaadt 535df930be7Sderaadt /* 536df930be7Sderaadt * Parse full path in file into 2 parts: directory and file names 537df930be7Sderaadt * Should leave final slash (/) at end of dir. 538df930be7Sderaadt */ 539df930be7Sderaadt static void 540e757c91eSderaadt extract_dir_and_name(Char *path, Char *dir, Char *name) 541df930be7Sderaadt { 542e757c91eSderaadt Char *p; 543df930be7Sderaadt 544df930be7Sderaadt p = Strrchr(path, '/'); 545df930be7Sderaadt if (p == NULL) { 546df930be7Sderaadt copyn(name, path, MAXNAMLEN); 547df930be7Sderaadt dir[0] = '\0'; 548df930be7Sderaadt } 549df930be7Sderaadt else { 550df930be7Sderaadt copyn(name, ++p, MAXNAMLEN); 551df930be7Sderaadt copyn(dir, path, p - path); 552df930be7Sderaadt } 553df930be7Sderaadt } 554df930be7Sderaadt 555df930be7Sderaadt static Char * 556e757c91eSderaadt getentry(DIR *dir_fd, int looking_for_lognames) 557df930be7Sderaadt { 558e757c91eSderaadt struct passwd *pw; 559e757c91eSderaadt struct dirent *dirp; 560df930be7Sderaadt 561df930be7Sderaadt if (looking_for_lognames) { 562df930be7Sderaadt if ((pw = getpwent()) == NULL) 563df930be7Sderaadt return (NULL); 564df930be7Sderaadt return (str2short(pw->pw_name)); 565df930be7Sderaadt } 566df930be7Sderaadt if ((dirp = readdir(dir_fd)) != NULL) 567df930be7Sderaadt return (str2short(dirp->d_name)); 568df930be7Sderaadt return (NULL); 569df930be7Sderaadt } 570df930be7Sderaadt 571df930be7Sderaadt static void 572e757c91eSderaadt free_items(Char **items, int numitems) 573df930be7Sderaadt { 574b1f6b197Smillert int i; 575df930be7Sderaadt 576b1f6b197Smillert for (i = 0; i < numitems; i++) 577acdb3202Smestre free(items[i]); 578acdb3202Smestre free(items); 579df930be7Sderaadt } 580df930be7Sderaadt 581df930be7Sderaadt #define FREE_ITEMS(items) { \ 582df930be7Sderaadt sigset_t sigset, osigset;\ 583df930be7Sderaadt \ 584df930be7Sderaadt sigemptyset(&sigset);\ 585df930be7Sderaadt sigaddset(&sigset, SIGINT);\ 586df930be7Sderaadt sigprocmask(SIG_BLOCK, &sigset, &osigset);\ 587b1f6b197Smillert free_items(items, numitems);\ 588df930be7Sderaadt sigprocmask(SIG_SETMASK, &osigset, NULL);\ 589df930be7Sderaadt } 590df930be7Sderaadt 591df930be7Sderaadt /* 592df930be7Sderaadt * Perform a RECOGNIZE or LIST command on string "word". 593df930be7Sderaadt */ 594df930be7Sderaadt static int 595e757c91eSderaadt tsearch(Char *word, COMMAND command, int max_word_length) 596df930be7Sderaadt { 597e757c91eSderaadt DIR *dir_fd; 598e757c91eSderaadt int numitems = 0, ignoring = TRUE, nignored = 0; 599e757c91eSderaadt int name_length, looking_for_lognames; 600b9fc9a72Sderaadt Char tilded_dir[PATH_MAX], dir[PATH_MAX]; 601df930be7Sderaadt Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 602df930be7Sderaadt Char *entry; 6039a5c8861Smillert Char **items = NULL; 6049a5c8861Smillert size_t maxitems = 0; 605df930be7Sderaadt 606df930be7Sderaadt looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 607df930be7Sderaadt if (looking_for_lognames) { 608df930be7Sderaadt (void) setpwent(); 609df930be7Sderaadt copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 610df930be7Sderaadt dir_fd = NULL; 611df930be7Sderaadt } 612df930be7Sderaadt else { 613df930be7Sderaadt extract_dir_and_name(word, dir, name); 614df930be7Sderaadt if (tilde(tilded_dir, dir) == 0) 615df930be7Sderaadt return (0); 616df930be7Sderaadt dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 617df930be7Sderaadt if (dir_fd == NULL) 618df930be7Sderaadt return (0); 619df930be7Sderaadt } 620df930be7Sderaadt 621df930be7Sderaadt again: /* search for matches */ 622df930be7Sderaadt name_length = Strlen(name); 623df930be7Sderaadt for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { 624df930be7Sderaadt if (!is_prefix(name, entry)) 625df930be7Sderaadt continue; 626df930be7Sderaadt /* Don't match . files on null prefix match */ 627df930be7Sderaadt if (name_length == 0 && entry[0] == '.' && 628df930be7Sderaadt !looking_for_lognames) 629df930be7Sderaadt continue; 630df930be7Sderaadt if (command == LIST) { 6319a5c8861Smillert if (numitems >= maxitems) { 6329a5c8861Smillert maxitems += 1024; 633a8627d2cSderaadt items = xreallocarray(items, maxitems, sizeof(*items)); 6349a5c8861Smillert } 635a8627d2cSderaadt items[numitems] = xreallocarray(NULL, (Strlen(entry) + 1), sizeof(Char)); 636df930be7Sderaadt copyn(items[numitems], entry, MAXNAMLEN); 637df930be7Sderaadt numitems++; 638df930be7Sderaadt } 639df930be7Sderaadt else { /* RECOGNIZE command */ 640df930be7Sderaadt if (ignoring && ignored(entry)) 641df930be7Sderaadt nignored++; 642df930be7Sderaadt else if (recognize(extended_name, 643df930be7Sderaadt entry, name_length, ++numitems)) 644df930be7Sderaadt break; 645df930be7Sderaadt } 646df930be7Sderaadt } 647df930be7Sderaadt if (ignoring && numitems == 0 && nignored > 0) { 648df930be7Sderaadt ignoring = FALSE; 649df930be7Sderaadt nignored = 0; 650df930be7Sderaadt if (looking_for_lognames) 651df930be7Sderaadt (void) setpwent(); 652df930be7Sderaadt else 653df930be7Sderaadt rewinddir(dir_fd); 654df930be7Sderaadt goto again; 655df930be7Sderaadt } 656df930be7Sderaadt 657df930be7Sderaadt if (looking_for_lognames) 658df930be7Sderaadt (void) endpwent(); 659df930be7Sderaadt else 660df930be7Sderaadt (void) closedir(dir_fd); 661df930be7Sderaadt if (numitems == 0) 662df930be7Sderaadt return (0); 663df930be7Sderaadt if (command == RECOGNIZE) { 664df930be7Sderaadt if (looking_for_lognames) 665df930be7Sderaadt copyn(word, STRtilde, 1); 666df930be7Sderaadt else 667df930be7Sderaadt /* put back dir part */ 668df930be7Sderaadt copyn(word, dir, max_word_length); 669df930be7Sderaadt /* add extended name */ 670df930be7Sderaadt catn(word, extended_name, max_word_length); 671df930be7Sderaadt return (numitems); 672df930be7Sderaadt } 673df930be7Sderaadt else { /* LIST */ 674f4d574c6Stedu qsort(items, numitems, sizeof(*items), 675c72b5b24Smillert (int (*)(const void *, const void *)) sortscmp); 676df930be7Sderaadt print_by_column(looking_for_lognames ? NULL : tilded_dir, 677df930be7Sderaadt items, numitems); 678df930be7Sderaadt if (items != NULL) 679df930be7Sderaadt FREE_ITEMS(items); 680df930be7Sderaadt } 681df930be7Sderaadt return (0); 682df930be7Sderaadt } 683df930be7Sderaadt 684df930be7Sderaadt /* 685df930be7Sderaadt * Object: extend what user typed up to an ambiguity. 686df930be7Sderaadt * Algorithm: 687df930be7Sderaadt * On first match, copy full entry (assume it'll be the only match) 688df930be7Sderaadt * On subsequent matches, shorten extended_name to the first 689df930be7Sderaadt * Character mismatch between extended_name and entry. 690df930be7Sderaadt * If we shorten it back to the prefix length, stop searching. 691df930be7Sderaadt */ 692df930be7Sderaadt static int 693e757c91eSderaadt recognize(Char *extended_name, Char *entry, int name_length, int numitems) 694df930be7Sderaadt { 695df930be7Sderaadt if (numitems == 1) /* 1st match */ 696df930be7Sderaadt copyn(extended_name, entry, MAXNAMLEN); 697df930be7Sderaadt else { /* 2nd & subsequent matches */ 698e757c91eSderaadt Char *x, *ent; 699e757c91eSderaadt int len = 0; 700df930be7Sderaadt 701df930be7Sderaadt x = extended_name; 702df930be7Sderaadt for (ent = entry; *x && *x == *ent++; x++, len++) 703df930be7Sderaadt continue; 704df930be7Sderaadt *x = '\0'; /* Shorten at 1st Char diff */ 705df930be7Sderaadt if (len == name_length) /* Ambiguous to prefix? */ 706df930be7Sderaadt return (-1); /* So stop now and save time */ 707df930be7Sderaadt } 708df930be7Sderaadt return (0); 709df930be7Sderaadt } 710df930be7Sderaadt 711df930be7Sderaadt /* 712df930be7Sderaadt * Return true if check matches initial Chars in template. 713df930be7Sderaadt * This differs from PWB imatch in that if check is null 714df930be7Sderaadt * it matches anything. 715df930be7Sderaadt */ 716df930be7Sderaadt static int 717e757c91eSderaadt is_prefix(Char *check, Char *template) 718df930be7Sderaadt { 719df930be7Sderaadt do 720df930be7Sderaadt if (*check == 0) 721df930be7Sderaadt return (TRUE); 722df930be7Sderaadt while (*check++ == *template++); 723df930be7Sderaadt return (FALSE); 724df930be7Sderaadt } 725df930be7Sderaadt 726df930be7Sderaadt /* 727df930be7Sderaadt * Return true if the Chars in template appear at the 728df930be7Sderaadt * end of check, I.e., are it's suffix. 729df930be7Sderaadt */ 730df930be7Sderaadt static int 731e757c91eSderaadt is_suffix(Char *check, Char *template) 732df930be7Sderaadt { 733e757c91eSderaadt Char *c, *t; 734df930be7Sderaadt 735df930be7Sderaadt for (c = check; *c++;) 736df930be7Sderaadt continue; 737df930be7Sderaadt for (t = template; *t++;) 738df930be7Sderaadt continue; 739df930be7Sderaadt for (;;) { 740df930be7Sderaadt if (t == template) 741df930be7Sderaadt return 1; 742df930be7Sderaadt if (c == check || *--t != *--c) 743df930be7Sderaadt return 0; 744df930be7Sderaadt } 745df930be7Sderaadt } 746df930be7Sderaadt 747df930be7Sderaadt int 748e757c91eSderaadt tenex(Char *inputline, int inputline_size) 749df930be7Sderaadt { 750c0d918cdSanton static struct { 751c0d918cdSanton int (*fn)(struct cmdline *, int); 752c0d918cdSanton int idx; 753c0d918cdSanton } keys[] = { 754c0d918cdSanton { cl_abort, VINTR }, 755c0d918cdSanton { cl_erasec, VERASE }, 756c0d918cdSanton { cl_erasew, VWERASE }, 757c0d918cdSanton { cl_kill, VKILL }, 758c0d918cdSanton { cl_list, VEOF }, 759c0d918cdSanton { cl_literal, VLNEXT }, 760c0d918cdSanton { cl_recognize, VEOL }, 761c0d918cdSanton { cl_reprint, VREPRINT }, 762c0d918cdSanton { cl_status, VSTATUS }, 763c0d918cdSanton { cl_insert, -1 } 764c0d918cdSanton }; 765c0d918cdSanton char buf[BUFSIZ]; 766c0d918cdSanton const struct termios *tio; 767c0d918cdSanton struct cmdline cl; 768c0d918cdSanton size_t i; 769c0d918cdSanton int c, ret; 770df930be7Sderaadt 771c0d918cdSanton tio = setup_tty(1); 772df930be7Sderaadt 773c0d918cdSanton memset(&cl, 0, sizeof(cl)); 774c0d918cdSanton cl.fdin = SHIN; 775c0d918cdSanton cl.fdout = SHOUT; 776c0d918cdSanton cl.buf = buf; 777c0d918cdSanton cl.size = sizeof(buf); 778c0d918cdSanton if (tio->c_lflag & ALTWERASE) 779c0d918cdSanton cl.flags |= CL_ALTWERASE; 780df930be7Sderaadt 781c0d918cdSanton for (;;) { 782c0d918cdSanton if ((c = cl_getc(&cl)) == 0) 783df930be7Sderaadt break; 784c0d918cdSanton 785c0d918cdSanton for (i = 0; keys[i].idx >= 0; i++) 786c0d918cdSanton if (CCEQ(tio->c_cc[keys[i].idx], c)) 787df930be7Sderaadt break; 788c0d918cdSanton ret = keys[i].fn(&cl, c); 789c0d918cdSanton cl_flush(&cl); 790c0d918cdSanton if (ret) 791c0d918cdSanton break; 792df930be7Sderaadt } 793df930be7Sderaadt 794c0d918cdSanton setup_tty(0); 795c0d918cdSanton 796c0d918cdSanton for (i = 0; i < cl.len; i++) 797c0d918cdSanton inputline[i] = cl.buf[i]; 798c0d918cdSanton inputline[i] = '\0'; 799c0d918cdSanton 800c0d918cdSanton return cl.len; 801df930be7Sderaadt } 802df930be7Sderaadt 803df930be7Sderaadt static int 804e757c91eSderaadt ignored(Char *entry) 805df930be7Sderaadt { 806df930be7Sderaadt struct varent *vp; 807e757c91eSderaadt Char **cp; 808df930be7Sderaadt 809df930be7Sderaadt if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 810df930be7Sderaadt return (FALSE); 811df930be7Sderaadt for (; *cp != NULL; cp++) 812df930be7Sderaadt if (is_suffix(entry, *cp)) 813df930be7Sderaadt return (TRUE); 814df930be7Sderaadt return (FALSE); 815df930be7Sderaadt } 816