1 1.31 christos /* $NetBSD: interactive.c,v 1.31 2021/08/29 09:17:58 christos Exp $ */ 2 1.9 cgd 3 1.1 cgd /* 4 1.4 mycroft * Copyright (c) 1985, 1993 5 1.4 mycroft * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.19 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.12 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.9 cgd #if 0 35 1.13 lukem static char sccsid[] = "@(#)interactive.c 8.5 (Berkeley) 5/1/95"; 36 1.9 cgd #else 37 1.31 christos __RCSID("$NetBSD: interactive.c,v 1.31 2021/08/29 09:17:58 christos Exp $"); 38 1.9 cgd #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.3 cgd #include <sys/param.h> 42 1.3 cgd #include <sys/time.h> 43 1.3 cgd #include <sys/stat.h> 44 1.3 cgd 45 1.4 mycroft #include <ufs/ufs/dinode.h> 46 1.4 mycroft #include <ufs/ufs/dir.h> 47 1.13 lukem #include <ufs/ffs/fs.h> 48 1.1 cgd #include <protocols/dumprestore.h> 49 1.3 cgd 50 1.1 cgd #include <setjmp.h> 51 1.3 cgd #include <glob.h> 52 1.29 christos #include <ctype.h> 53 1.3 cgd #include <stdio.h> 54 1.3 cgd #include <stdlib.h> 55 1.3 cgd #include <string.h> 56 1.3 cgd 57 1.3 cgd #include "restore.h" 58 1.3 cgd #include "extern.h" 59 1.1 cgd 60 1.1 cgd #define round(a, b) (((a) + (b) - 1) / (b) * (b)) 61 1.1 cgd 62 1.1 cgd /* 63 1.1 cgd * Things to handle interruptions. 64 1.1 cgd */ 65 1.3 cgd static int runshell; 66 1.1 cgd static jmp_buf reset; 67 1.1 cgd static char *nextarg = NULL; 68 1.1 cgd 69 1.1 cgd /* 70 1.1 cgd * Structure and routines associated with listing directories. 71 1.1 cgd */ 72 1.1 cgd struct afile { 73 1.1 cgd ino_t fnum; /* inode number of file */ 74 1.1 cgd char *fname; /* file name */ 75 1.26 lukem size_t len; /* name length */ 76 1.3 cgd char prefix; /* prefix character */ 77 1.3 cgd char postfix; /* postfix character */ 78 1.1 cgd }; 79 1.1 cgd struct arglist { 80 1.3 cgd int freeglob; /* glob structure needs to be freed */ 81 1.3 cgd int argcnt; /* next globbed argument to return */ 82 1.3 cgd glob_t glob; /* globbing information */ 83 1.3 cgd char *cmd; /* the current command */ 84 1.1 cgd }; 85 1.3 cgd 86 1.21 xtraeme static char *copynext(char *, char *); 87 1.21 xtraeme static int fcmp(const void *, const void *); 88 1.21 xtraeme static void formatf(struct afile *, int); 89 1.29 christos static void getcmd(char *, char *, char *, size_t, struct arglist *); 90 1.21 xtraeme struct dirent *glob_readdir(RST_DIR *dirp); 91 1.21 xtraeme static int glob_stat(const char *, struct stat *); 92 1.21 xtraeme static void mkentry(char *, struct direct *, struct afile *); 93 1.21 xtraeme static void printlist(char *, char *); 94 1.1 cgd 95 1.1 cgd /* 96 1.1 cgd * Read and execute commands from the terminal. 97 1.1 cgd */ 98 1.3 cgd void 99 1.21 xtraeme runcmdshell(void) 100 1.1 cgd { 101 1.10 lukem struct entry *np; 102 1.1 cgd ino_t ino; 103 1.3 cgd struct arglist arglist; 104 1.1 cgd char curdir[MAXPATHLEN]; 105 1.1 cgd char name[MAXPATHLEN]; 106 1.1 cgd char cmd[BUFSIZ]; 107 1.1 cgd 108 1.3 cgd arglist.freeglob = 0; 109 1.3 cgd arglist.argcnt = 0; 110 1.3 cgd arglist.glob.gl_flags = GLOB_ALTDIRFUNC; 111 1.3 cgd arglist.glob.gl_opendir = (void *)rst_opendir; 112 1.3 cgd arglist.glob.gl_readdir = (void *)glob_readdir; 113 1.3 cgd arglist.glob.gl_closedir = (void *)rst_closedir; 114 1.3 cgd arglist.glob.gl_lstat = glob_stat; 115 1.3 cgd arglist.glob.gl_stat = glob_stat; 116 1.29 christos canon("/", curdir, sizeof(curdir)); 117 1.1 cgd loop: 118 1.1 cgd if (setjmp(reset) != 0) { 119 1.3 cgd if (arglist.freeglob != 0) { 120 1.3 cgd arglist.freeglob = 0; 121 1.3 cgd arglist.argcnt = 0; 122 1.3 cgd globfree(&arglist.glob); 123 1.3 cgd } 124 1.1 cgd nextarg = NULL; 125 1.1 cgd volno = 0; 126 1.1 cgd } 127 1.3 cgd runshell = 1; 128 1.29 christos getcmd(curdir, cmd, name, sizeof(name), &arglist); 129 1.1 cgd switch (cmd[0]) { 130 1.1 cgd /* 131 1.1 cgd * Add elements to the extraction list. 132 1.1 cgd */ 133 1.1 cgd case 'a': 134 1.1 cgd if (strncmp(cmd, "add", strlen(cmd)) != 0) 135 1.1 cgd goto bad; 136 1.1 cgd ino = dirlookup(name); 137 1.1 cgd if (ino == 0) 138 1.1 cgd break; 139 1.27 dholland if (ino == UFS_ROOTINO) 140 1.20 fredb dotflag = 1; 141 1.1 cgd if (mflag) 142 1.1 cgd pathcheck(name); 143 1.1 cgd treescan(name, ino, addfile); 144 1.1 cgd break; 145 1.1 cgd /* 146 1.1 cgd * Change working directory. 147 1.1 cgd */ 148 1.1 cgd case 'c': 149 1.1 cgd if (strncmp(cmd, "cd", strlen(cmd)) != 0) 150 1.1 cgd goto bad; 151 1.1 cgd ino = dirlookup(name); 152 1.1 cgd if (ino == 0) 153 1.1 cgd break; 154 1.1 cgd if (inodetype(ino) == LEAF) { 155 1.1 cgd fprintf(stderr, "%s: not a directory\n", name); 156 1.1 cgd break; 157 1.1 cgd } 158 1.1 cgd (void) strcpy(curdir, name); 159 1.1 cgd break; 160 1.1 cgd /* 161 1.1 cgd * Delete elements from the extraction list. 162 1.1 cgd */ 163 1.1 cgd case 'd': 164 1.1 cgd if (strncmp(cmd, "delete", strlen(cmd)) != 0) 165 1.1 cgd goto bad; 166 1.1 cgd np = lookupname(name); 167 1.3 cgd if (np == NULL || (np->e_flags & NEW) == 0) { 168 1.1 cgd fprintf(stderr, "%s: not on extraction list\n", name); 169 1.1 cgd break; 170 1.1 cgd } 171 1.1 cgd treescan(name, np->e_ino, deletefile); 172 1.1 cgd break; 173 1.1 cgd /* 174 1.1 cgd * Extract the requested list. 175 1.1 cgd */ 176 1.1 cgd case 'e': 177 1.1 cgd if (strncmp(cmd, "extract", strlen(cmd)) != 0) 178 1.1 cgd goto bad; 179 1.1 cgd createfiles(); 180 1.1 cgd createlinks(); 181 1.3 cgd setdirmodes(0); 182 1.1 cgd if (dflag) 183 1.1 cgd checkrestore(); 184 1.1 cgd volno = 0; 185 1.1 cgd break; 186 1.1 cgd /* 187 1.1 cgd * List available commands. 188 1.1 cgd */ 189 1.1 cgd case 'h': 190 1.1 cgd if (strncmp(cmd, "help", strlen(cmd)) != 0) 191 1.1 cgd goto bad; 192 1.28 mrg /* FALLTHROUGH */ 193 1.1 cgd case '?': 194 1.14 enami fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 195 1.1 cgd "Available commands are:\n", 196 1.1 cgd "\tls [arg] - list directory\n", 197 1.1 cgd "\tcd arg - change directory\n", 198 1.1 cgd "\tpwd - print current directory\n", 199 1.1 cgd "\tadd [arg] - add `arg' to list of", 200 1.1 cgd " files to be extracted\n", 201 1.1 cgd "\tdelete [arg] - delete `arg' from", 202 1.1 cgd " list of files to be extracted\n", 203 1.1 cgd "\textract - extract requested files\n", 204 1.1 cgd "\tsetmodes - set modes of requested directories\n", 205 1.14 enami "\tquit or xit - immediately exit program\n", 206 1.1 cgd "\twhat - list dump header information\n", 207 1.1 cgd "\tverbose - toggle verbose flag", 208 1.1 cgd " (useful with ``ls'')\n", 209 1.1 cgd "\thelp or `?' - print this list\n", 210 1.14 enami "\tDebug - turn on debugging\n", 211 1.1 cgd "If no `arg' is supplied, the current", 212 1.1 cgd " directory is used\n"); 213 1.1 cgd break; 214 1.1 cgd /* 215 1.1 cgd * List a directory. 216 1.1 cgd */ 217 1.1 cgd case 'l': 218 1.1 cgd if (strncmp(cmd, "ls", strlen(cmd)) != 0) 219 1.1 cgd goto bad; 220 1.3 cgd printlist(name, curdir); 221 1.1 cgd break; 222 1.1 cgd /* 223 1.1 cgd * Print current directory. 224 1.1 cgd */ 225 1.1 cgd case 'p': 226 1.1 cgd if (strncmp(cmd, "pwd", strlen(cmd)) != 0) 227 1.1 cgd goto bad; 228 1.1 cgd if (curdir[1] == '\0') 229 1.1 cgd fprintf(stderr, "/\n"); 230 1.1 cgd else 231 1.1 cgd fprintf(stderr, "%s\n", &curdir[1]); 232 1.1 cgd break; 233 1.1 cgd /* 234 1.1 cgd * Quit. 235 1.1 cgd */ 236 1.1 cgd case 'q': 237 1.1 cgd if (strncmp(cmd, "quit", strlen(cmd)) != 0) 238 1.1 cgd goto bad; 239 1.1 cgd return; 240 1.1 cgd case 'x': 241 1.1 cgd if (strncmp(cmd, "xit", strlen(cmd)) != 0) 242 1.1 cgd goto bad; 243 1.1 cgd return; 244 1.1 cgd /* 245 1.1 cgd * Toggle verbose mode. 246 1.1 cgd */ 247 1.1 cgd case 'v': 248 1.1 cgd if (strncmp(cmd, "verbose", strlen(cmd)) != 0) 249 1.1 cgd goto bad; 250 1.1 cgd if (vflag) { 251 1.1 cgd fprintf(stderr, "verbose mode off\n"); 252 1.1 cgd vflag = 0; 253 1.1 cgd break; 254 1.1 cgd } 255 1.1 cgd fprintf(stderr, "verbose mode on\n"); 256 1.1 cgd vflag++; 257 1.1 cgd break; 258 1.1 cgd /* 259 1.1 cgd * Just restore requested directory modes. 260 1.1 cgd */ 261 1.1 cgd case 's': 262 1.1 cgd if (strncmp(cmd, "setmodes", strlen(cmd)) != 0) 263 1.1 cgd goto bad; 264 1.3 cgd setdirmodes(FORCE); 265 1.1 cgd break; 266 1.1 cgd /* 267 1.1 cgd * Print out dump header information. 268 1.1 cgd */ 269 1.1 cgd case 'w': 270 1.1 cgd if (strncmp(cmd, "what", strlen(cmd)) != 0) 271 1.1 cgd goto bad; 272 1.1 cgd printdumpinfo(); 273 1.1 cgd break; 274 1.1 cgd /* 275 1.1 cgd * Turn on debugging. 276 1.1 cgd */ 277 1.1 cgd case 'D': 278 1.1 cgd if (strncmp(cmd, "Debug", strlen(cmd)) != 0) 279 1.1 cgd goto bad; 280 1.1 cgd if (dflag) { 281 1.1 cgd fprintf(stderr, "debugging mode off\n"); 282 1.1 cgd dflag = 0; 283 1.1 cgd break; 284 1.1 cgd } 285 1.1 cgd fprintf(stderr, "debugging mode on\n"); 286 1.1 cgd dflag++; 287 1.1 cgd break; 288 1.1 cgd /* 289 1.1 cgd * Unknown command. 290 1.1 cgd */ 291 1.1 cgd default: 292 1.1 cgd bad: 293 1.1 cgd fprintf(stderr, "%s: unknown command; type ? for help\n", cmd); 294 1.1 cgd break; 295 1.1 cgd } 296 1.1 cgd goto loop; 297 1.1 cgd } 298 1.1 cgd 299 1.1 cgd /* 300 1.1 cgd * Read and parse an interactive command. 301 1.1 cgd * The first word on the line is assigned to "cmd". If 302 1.1 cgd * there are no arguments on the command line, then "curdir" 303 1.1 cgd * is returned as the argument. If there are arguments 304 1.1 cgd * on the line they are returned one at a time on each 305 1.1 cgd * successive call to getcmd. Each argument is first assigned 306 1.1 cgd * to "name". If it does not start with "/" the pathname in 307 1.1 cgd * "curdir" is prepended to it. Finally "canon" is called to 308 1.1 cgd * eliminate any embedded ".." components. 309 1.1 cgd */ 310 1.3 cgd static void 311 1.29 christos getcmd(char *curdir, char *cmd, char *name, size_t size, struct arglist *ap) 312 1.1 cgd { 313 1.10 lukem char *cp; 314 1.1 cgd static char input[BUFSIZ]; 315 1.29 christos char output[BUFSIZ * 2]; 316 1.15 lukem int globretval; 317 1.1 cgd # define rawname input /* save space by reusing input buffer */ 318 1.1 cgd 319 1.1 cgd /* 320 1.1 cgd * Check to see if still processing arguments. 321 1.1 cgd */ 322 1.3 cgd if (ap->argcnt > 0) 323 1.3 cgd goto retnext; 324 1.1 cgd if (nextarg != NULL) 325 1.1 cgd goto getnext; 326 1.1 cgd /* 327 1.1 cgd * Read a command line and trim off trailing white space. 328 1.1 cgd */ 329 1.1 cgd do { 330 1.18 cgd fprintf(stderr, "%s > ", getprogname()); 331 1.1 cgd (void) fflush(stderr); 332 1.1 cgd (void) fgets(input, BUFSIZ, terminal); 333 1.1 cgd } while (!feof(terminal) && input[0] == '\n'); 334 1.1 cgd if (feof(terminal)) { 335 1.29 christos (void) strlcpy(cmd, "quit", size); 336 1.1 cgd return; 337 1.1 cgd } 338 1.1 cgd for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) 339 1.1 cgd /* trim off trailing white space and newline */; 340 1.1 cgd *++cp = '\0'; 341 1.1 cgd /* 342 1.1 cgd * Copy the command into "cmd". 343 1.1 cgd */ 344 1.1 cgd cp = copynext(input, cmd); 345 1.1 cgd ap->cmd = cmd; 346 1.1 cgd /* 347 1.1 cgd * If no argument, use curdir as the default. 348 1.1 cgd */ 349 1.1 cgd if (*cp == '\0') { 350 1.29 christos (void) strlcpy(name, curdir, size); 351 1.1 cgd return; 352 1.1 cgd } 353 1.1 cgd nextarg = cp; 354 1.1 cgd /* 355 1.1 cgd * Find the next argument. 356 1.1 cgd */ 357 1.1 cgd getnext: 358 1.1 cgd cp = copynext(nextarg, rawname); 359 1.1 cgd if (*cp == '\0') 360 1.1 cgd nextarg = NULL; 361 1.1 cgd else 362 1.1 cgd nextarg = cp; 363 1.1 cgd /* 364 1.3 cgd * If it is an absolute pathname, canonicalize it and return it. 365 1.1 cgd */ 366 1.1 cgd if (rawname[0] == '/') { 367 1.29 christos canon(rawname, name, size); 368 1.1 cgd } else { 369 1.1 cgd /* 370 1.1 cgd * For relative pathnames, prepend the current directory to 371 1.1 cgd * it then canonicalize and return it. 372 1.1 cgd */ 373 1.29 christos snprintf(output, sizeof(output), "%s/%s", curdir, rawname); 374 1.30 christos canon(output, name, size); 375 1.1 cgd } 376 1.15 lukem if ((globretval = glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob)) < 0) { 377 1.15 lukem fprintf(stderr, "%s: %s: ", ap->cmd, name); 378 1.15 lukem switch (globretval) { 379 1.15 lukem case GLOB_NOSPACE: 380 1.15 lukem fprintf(stderr, "out of memory\n"); 381 1.15 lukem break; 382 1.15 lukem case GLOB_NOMATCH: 383 1.15 lukem fprintf(stderr, "no filename match.\n"); 384 1.15 lukem break; 385 1.15 lukem case GLOB_ABORTED: 386 1.15 lukem fprintf(stderr, "glob() aborted.\n"); 387 1.15 lukem break; 388 1.15 lukem default: 389 1.15 lukem fprintf(stderr, "unknown error!\n"); 390 1.15 lukem break; 391 1.15 lukem } 392 1.15 lukem } 393 1.3 cgd if (ap->glob.gl_pathc == 0) 394 1.3 cgd return; 395 1.3 cgd ap->freeglob = 1; 396 1.3 cgd ap->argcnt = ap->glob.gl_pathc; 397 1.3 cgd 398 1.3 cgd retnext: 399 1.29 christos strlcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size); 400 1.3 cgd if (--ap->argcnt == 0) { 401 1.3 cgd ap->freeglob = 0; 402 1.3 cgd globfree(&ap->glob); 403 1.3 cgd } 404 1.1 cgd # undef rawname 405 1.1 cgd } 406 1.1 cgd 407 1.1 cgd /* 408 1.1 cgd * Strip off the next token of the input. 409 1.1 cgd */ 410 1.3 cgd static char * 411 1.21 xtraeme copynext(char *input, char *output) 412 1.1 cgd { 413 1.10 lukem char *cp, *bp; 414 1.1 cgd char quote; 415 1.1 cgd 416 1.1 cgd for (cp = input; *cp == ' ' || *cp == '\t'; cp++) 417 1.1 cgd /* skip to argument */; 418 1.1 cgd bp = output; 419 1.1 cgd while (*cp != ' ' && *cp != '\t' && *cp != '\0') { 420 1.1 cgd /* 421 1.1 cgd * Handle back slashes. 422 1.1 cgd */ 423 1.1 cgd if (*cp == '\\') { 424 1.1 cgd if (*++cp == '\0') { 425 1.1 cgd fprintf(stderr, 426 1.1 cgd "command lines cannot be continued\n"); 427 1.1 cgd continue; 428 1.1 cgd } 429 1.1 cgd *bp++ = *cp++; 430 1.1 cgd continue; 431 1.1 cgd } 432 1.1 cgd /* 433 1.1 cgd * The usual unquoted case. 434 1.1 cgd */ 435 1.1 cgd if (*cp != '\'' && *cp != '"') { 436 1.1 cgd *bp++ = *cp++; 437 1.1 cgd continue; 438 1.1 cgd } 439 1.1 cgd /* 440 1.1 cgd * Handle single and double quotes. 441 1.1 cgd */ 442 1.1 cgd quote = *cp++; 443 1.1 cgd while (*cp != quote && *cp != '\0') 444 1.16 erh *bp++ = *cp++; 445 1.1 cgd if (*cp++ == '\0') { 446 1.1 cgd fprintf(stderr, "missing %c\n", quote); 447 1.1 cgd cp--; 448 1.1 cgd continue; 449 1.1 cgd } 450 1.1 cgd } 451 1.1 cgd *bp = '\0'; 452 1.1 cgd return (cp); 453 1.1 cgd } 454 1.1 cgd 455 1.1 cgd /* 456 1.1 cgd * Canonicalize file names to always start with ``./'' and 457 1.1 cgd * remove any imbedded "." and ".." components. 458 1.1 cgd */ 459 1.3 cgd void 460 1.29 christos canon(const char *rawname, char *canonname, size_t len) 461 1.1 cgd { 462 1.10 lukem char *cp, *np; 463 1.1 cgd 464 1.1 cgd if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 465 1.1 cgd (void) strcpy(canonname, ""); 466 1.1 cgd else if (rawname[0] == '/') 467 1.1 cgd (void) strcpy(canonname, "."); 468 1.1 cgd else 469 1.1 cgd (void) strcpy(canonname, "./"); 470 1.29 christos if (strlen(canonname) + strlen(rawname) >= len) { 471 1.29 christos fprintf(stderr, "canonname: not enough buffer space\n"); 472 1.29 christos exit(1); 473 1.29 christos } 474 1.29 christos 475 1.1 cgd (void) strcat(canonname, rawname); 476 1.1 cgd /* 477 1.1 cgd * Eliminate multiple and trailing '/'s 478 1.1 cgd */ 479 1.1 cgd for (cp = np = canonname; *np != '\0'; cp++) { 480 1.1 cgd *cp = *np++; 481 1.1 cgd while (*cp == '/' && *np == '/') 482 1.1 cgd np++; 483 1.1 cgd } 484 1.1 cgd *cp = '\0'; 485 1.1 cgd if (*--cp == '/') 486 1.1 cgd *cp = '\0'; 487 1.1 cgd /* 488 1.1 cgd * Eliminate extraneous "." and ".." from pathnames. 489 1.1 cgd */ 490 1.1 cgd for (np = canonname; *np != '\0'; ) { 491 1.1 cgd np++; 492 1.1 cgd cp = np; 493 1.1 cgd while (*np != '/' && *np != '\0') 494 1.1 cgd np++; 495 1.1 cgd if (np - cp == 1 && *cp == '.') { 496 1.1 cgd cp--; 497 1.1 cgd (void) strcpy(cp, np); 498 1.1 cgd np = cp; 499 1.1 cgd } 500 1.1 cgd if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 501 1.1 cgd cp--; 502 1.1 cgd while (cp > &canonname[1] && *--cp != '/') 503 1.1 cgd /* find beginning of name */; 504 1.1 cgd (void) strcpy(cp, np); 505 1.1 cgd np = cp; 506 1.1 cgd } 507 1.1 cgd } 508 1.1 cgd } 509 1.1 cgd 510 1.1 cgd /* 511 1.1 cgd * Do an "ls" style listing of a directory 512 1.1 cgd */ 513 1.3 cgd static void 514 1.21 xtraeme printlist(char *name, char *basename) 515 1.1 cgd { 516 1.10 lukem struct afile *fp, *list, *listp; 517 1.10 lukem struct direct *dp; 518 1.1 cgd struct afile single; 519 1.3 cgd RST_DIR *dirp; 520 1.6 mycroft int entries, len, namelen; 521 1.6 mycroft char locname[MAXPATHLEN + 1]; 522 1.1 cgd 523 1.3 cgd dp = pathsearch(name); 524 1.11 lukem listp = NULL; 525 1.6 mycroft if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) || 526 1.27 dholland (!vflag && dp->d_ino == UFS_WINO)) 527 1.3 cgd return; 528 1.1 cgd if ((dirp = rst_opendir(name)) == NULL) { 529 1.3 cgd entries = 1; 530 1.3 cgd list = &single; 531 1.6 mycroft mkentry(name, dp, list); 532 1.3 cgd len = strlen(basename) + 1; 533 1.3 cgd if (strlen(name) - len > single.len) { 534 1.3 cgd freename(single.fname); 535 1.3 cgd single.fname = savename(&name[len]); 536 1.3 cgd single.len = strlen(single.fname); 537 1.3 cgd } 538 1.1 cgd } else { 539 1.3 cgd entries = 0; 540 1.11 lukem while ((dp = rst_readdir(dirp)) != NULL) 541 1.3 cgd entries++; 542 1.3 cgd rst_closedir(dirp); 543 1.3 cgd list = (struct afile *)malloc(entries * sizeof(struct afile)); 544 1.3 cgd if (list == NULL) { 545 1.3 cgd fprintf(stderr, "ls: out of memory\n"); 546 1.3 cgd return; 547 1.3 cgd } 548 1.3 cgd if ((dirp = rst_opendir(name)) == NULL) 549 1.3 cgd panic("directory reopen failed\n"); 550 1.1 cgd fprintf(stderr, "%s:\n", name); 551 1.3 cgd entries = 0; 552 1.3 cgd listp = list; 553 1.29 christos namelen = snprintf(locname, sizeof(locname), "%s/", name); 554 1.11 lukem while ((dp = rst_readdir(dirp)) != NULL) { 555 1.3 cgd if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) 556 1.1 cgd continue; 557 1.27 dholland if (!vflag && (dp->d_ino == UFS_WINO || 558 1.6 mycroft strcmp(dp->d_name, ".") == 0 || 559 1.1 cgd strcmp(dp->d_name, "..") == 0)) 560 1.1 cgd continue; 561 1.6 mycroft locname[namelen] = '\0'; 562 1.6 mycroft if (namelen + dp->d_namlen >= MAXPATHLEN) { 563 1.6 mycroft fprintf(stderr, "%s%s: name exceeds %d char\n", 564 1.6 mycroft locname, dp->d_name, MAXPATHLEN); 565 1.6 mycroft } else { 566 1.29 christos (void)strlcat(locname, dp->d_name, MAXPATHLEN); 567 1.6 mycroft mkentry(locname, dp, listp++); 568 1.6 mycroft entries++; 569 1.6 mycroft } 570 1.1 cgd } 571 1.3 cgd rst_closedir(dirp); 572 1.3 cgd if (entries == 0) { 573 1.3 cgd fprintf(stderr, "\n"); 574 1.3 cgd free(list); 575 1.3 cgd return; 576 1.3 cgd } 577 1.3 cgd qsort((char *)list, entries, sizeof(struct afile), fcmp); 578 1.3 cgd } 579 1.3 cgd formatf(list, entries); 580 1.3 cgd if (dirp != NULL) { 581 1.3 cgd for (fp = listp - 1; fp >= list; fp--) 582 1.1 cgd freename(fp->fname); 583 1.3 cgd fprintf(stderr, "\n"); 584 1.3 cgd free(list); 585 1.1 cgd } 586 1.1 cgd } 587 1.1 cgd 588 1.1 cgd /* 589 1.1 cgd * Read the contents of a directory. 590 1.1 cgd */ 591 1.3 cgd static void 592 1.21 xtraeme mkentry(char *name, struct direct *dp, struct afile *fp) 593 1.1 cgd { 594 1.3 cgd char *cp; 595 1.3 cgd struct entry *np; 596 1.3 cgd 597 1.3 cgd fp->fnum = dp->d_ino; 598 1.3 cgd fp->fname = savename(dp->d_name); 599 1.3 cgd for (cp = fp->fname; *cp; cp++) 600 1.31 christos if (!vflag && !isprint((unsigned char)*cp)) 601 1.3 cgd *cp = '?'; 602 1.3 cgd fp->len = cp - fp->fname; 603 1.3 cgd if (dflag && TSTINO(fp->fnum, dumpmap) == 0) 604 1.3 cgd fp->prefix = '^'; 605 1.6 mycroft else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW)) 606 1.3 cgd fp->prefix = '*'; 607 1.3 cgd else 608 1.3 cgd fp->prefix = ' '; 609 1.4 mycroft switch(dp->d_type) { 610 1.3 cgd 611 1.3 cgd default: 612 1.4 mycroft fprintf(stderr, "Warning: undefined file type %d\n", 613 1.4 mycroft dp->d_type); 614 1.29 christos /* FALLTHROUGH */ 615 1.3 cgd case DT_REG: 616 1.3 cgd fp->postfix = ' '; 617 1.3 cgd break; 618 1.3 cgd 619 1.3 cgd case DT_LNK: 620 1.3 cgd fp->postfix = '@'; 621 1.3 cgd break; 622 1.3 cgd 623 1.3 cgd case DT_FIFO: 624 1.3 cgd case DT_SOCK: 625 1.3 cgd fp->postfix = '='; 626 1.3 cgd break; 627 1.3 cgd 628 1.3 cgd case DT_CHR: 629 1.3 cgd case DT_BLK: 630 1.3 cgd fp->postfix = '#'; 631 1.3 cgd break; 632 1.1 cgd 633 1.6 mycroft case DT_WHT: 634 1.6 mycroft fp->postfix = '%'; 635 1.6 mycroft break; 636 1.6 mycroft 637 1.3 cgd case DT_UNKNOWN: 638 1.3 cgd case DT_DIR: 639 1.3 cgd if (inodetype(dp->d_ino) == NODE) 640 1.3 cgd fp->postfix = '/'; 641 1.3 cgd else 642 1.3 cgd fp->postfix = ' '; 643 1.3 cgd break; 644 1.1 cgd } 645 1.3 cgd return; 646 1.1 cgd } 647 1.1 cgd 648 1.1 cgd /* 649 1.1 cgd * Print out a pretty listing of a directory 650 1.1 cgd */ 651 1.3 cgd static void 652 1.21 xtraeme formatf(struct afile *list, int nentry) 653 1.3 cgd { 654 1.10 lukem struct afile *fp, *endlist; 655 1.26 lukem int haveprefix, havepostfix; 656 1.26 lukem ino_t bigino; 657 1.26 lukem size_t width, w; 658 1.26 lukem int i, j, precision, columns, lines; 659 1.3 cgd 660 1.3 cgd width = 0; 661 1.3 cgd haveprefix = 0; 662 1.3 cgd havepostfix = 0; 663 1.11 lukem precision = 0; 664 1.27 dholland bigino = UFS_ROOTINO; 665 1.3 cgd endlist = &list[nentry]; 666 1.3 cgd for (fp = &list[0]; fp < endlist; fp++) { 667 1.3 cgd if (bigino < fp->fnum) 668 1.3 cgd bigino = fp->fnum; 669 1.3 cgd if (width < fp->len) 670 1.3 cgd width = fp->len; 671 1.3 cgd if (fp->prefix != ' ') 672 1.3 cgd haveprefix = 1; 673 1.3 cgd if (fp->postfix != ' ') 674 1.3 cgd havepostfix = 1; 675 1.3 cgd } 676 1.3 cgd if (haveprefix) 677 1.3 cgd width++; 678 1.3 cgd if (havepostfix) 679 1.3 cgd width++; 680 1.3 cgd if (vflag) { 681 1.3 cgd for (precision = 0, i = bigino; i > 0; i /= 10) 682 1.3 cgd precision++; 683 1.3 cgd width += precision + 1; 684 1.1 cgd } 685 1.3 cgd width++; 686 1.3 cgd columns = 81 / width; 687 1.1 cgd if (columns == 0) 688 1.1 cgd columns = 1; 689 1.1 cgd lines = (nentry + columns - 1) / columns; 690 1.1 cgd for (i = 0; i < lines; i++) { 691 1.1 cgd for (j = 0; j < columns; j++) { 692 1.3 cgd fp = &list[j * lines + i]; 693 1.3 cgd if (vflag) { 694 1.29 christos fprintf(stderr, "%*ju ", precision, 695 1.29 christos (uintmax_t)fp->fnum); 696 1.3 cgd fp->len += precision + 1; 697 1.3 cgd } 698 1.3 cgd if (haveprefix) { 699 1.3 cgd putc(fp->prefix, stderr); 700 1.3 cgd fp->len++; 701 1.3 cgd } 702 1.3 cgd fprintf(stderr, "%s", fp->fname); 703 1.3 cgd if (havepostfix) { 704 1.3 cgd putc(fp->postfix, stderr); 705 1.3 cgd fp->len++; 706 1.3 cgd } 707 1.3 cgd if (fp + lines >= endlist) { 708 1.1 cgd fprintf(stderr, "\n"); 709 1.1 cgd break; 710 1.1 cgd } 711 1.3 cgd for (w = fp->len; w < width; w++) 712 1.3 cgd putc(' ', stderr); 713 1.1 cgd } 714 1.1 cgd } 715 1.1 cgd } 716 1.1 cgd 717 1.1 cgd /* 718 1.3 cgd * Skip over directory entries that are not on the tape 719 1.3 cgd * 720 1.3 cgd * First have to get definition of a dirent. 721 1.1 cgd */ 722 1.3 cgd #undef DIRBLKSIZ 723 1.3 cgd #include <dirent.h> 724 1.3 cgd #undef d_ino 725 1.3 cgd 726 1.3 cgd struct dirent * 727 1.21 xtraeme glob_readdir(RST_DIR *dirp) 728 1.1 cgd { 729 1.3 cgd struct direct *dp; 730 1.3 cgd static struct dirent adirent; 731 1.1 cgd 732 1.3 cgd while ((dp = rst_readdir(dirp)) != NULL) { 733 1.27 dholland if (!vflag && dp->d_fileno == UFS_WINO) 734 1.3 cgd continue; 735 1.24 christos if (dflag || TSTINO(dp->d_fileno, dumpmap)) 736 1.3 cgd break; 737 1.3 cgd } 738 1.3 cgd if (dp == NULL) 739 1.3 cgd return (NULL); 740 1.24 christos adirent.d_fileno = dp->d_fileno; 741 1.3 cgd adirent.d_namlen = dp->d_namlen; 742 1.13 lukem memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1); 743 1.3 cgd return (&adirent); 744 1.1 cgd } 745 1.1 cgd 746 1.1 cgd /* 747 1.3 cgd * Return st_mode information in response to stat or lstat calls 748 1.1 cgd */ 749 1.3 cgd static int 750 1.21 xtraeme glob_stat(const char *name, struct stat *stp) 751 1.1 cgd { 752 1.10 lukem struct direct *dp; 753 1.1 cgd 754 1.3 cgd dp = pathsearch(name); 755 1.24 christos if (dp == NULL || (!dflag && TSTINO(dp->d_fileno, dumpmap) == 0) || 756 1.27 dholland (!vflag && dp->d_fileno == UFS_WINO)) 757 1.3 cgd return (-1); 758 1.24 christos if (inodetype(dp->d_fileno) == NODE) 759 1.7 mycroft stp->st_mode = S_IFDIR; 760 1.1 cgd else 761 1.7 mycroft stp->st_mode = S_IFREG; 762 1.3 cgd return (0); 763 1.3 cgd } 764 1.3 cgd 765 1.3 cgd /* 766 1.3 cgd * Comparison routine for qsort. 767 1.3 cgd */ 768 1.3 cgd static int 769 1.21 xtraeme fcmp(const void *f1, const void *f2) 770 1.3 cgd { 771 1.29 christos return (strcoll(((const struct afile *)f1)->fname, 772 1.22 christos ((const struct afile *)f2)->fname)); 773 1.1 cgd } 774 1.1 cgd 775 1.1 cgd /* 776 1.1 cgd * respond to interrupts 777 1.1 cgd */ 778 1.1 cgd void 779 1.25 christos /*ARGSUSED*/ 780 1.25 christos onintr(int signo __unused) 781 1.1 cgd { 782 1.3 cgd if (command == 'i' && runshell) 783 1.1 cgd longjmp(reset, 1); 784 1.1 cgd if (reply("restore interrupted, continue") == FAIL) 785 1.8 mycroft exit(1); 786 1.1 cgd } 787