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